13bool parseSegment(
const std::string& seg, std::string& key, std::vector<size_t>& indexes) {
16 if (seg.empty())
return false;
20 while (i < seg.size() && seg[i] !=
'[') key.push_back(seg[i++]);
22 if (key.empty() && (i >= seg.size()))
return false;
24 while (i < seg.size()) {
25 if (seg[i] !=
'[')
return false;
26 const size_t close = seg.find(
']', i);
27 if (close == std::string::npos)
return false;
28 const std::string
idx = seg.substr(i + 1, close - i - 1);
29 if (
idx.empty() ||
idx.find_first_not_of(
"0123456789") != std::string::npos)
return false;
30 indexes.push_back(
static_cast<size_t>(std::stoull(
idx)));
65 for (
auto& kv : obj_) {
66 if (kv.first == key) {
67 kv.second = std::move(
v);
71 obj_.emplace_back(key, std::move(
v));
75 for (
const auto& kv : obj_)
76 if (kv.first == key)
return &kv.second;
82 if (kv.first == key)
return &kv.second;
87 std::vector<std::string> out;
88 out.reserve(obj_.size());
89 for (
const auto& kv : obj_) out.push_back(kv.first);
97 const size_t dot = dottedPath.
find(
'.', start);
98 const bool last = (dot == std::string::npos);
99 const std::string seg = dottedPath.substr(start, last ? std::string::npos : dot - start);
102 std::vector<size_t> indexes;
103 if (!parseSegment(seg, key, indexes))
return nullptr;
105 cur = cur->
find(key);
106 if (!cur)
return nullptr;
108 for (
const size_t idx : indexes) {
109 if (!cur->
isArray() ||
idx >= cur->arr_.size())
return nullptr;
110 cur = &cur->arr_[
idx];
112 if (last)
return cur;
122 std::vector<std::string> segs;
125 const size_t dot = dottedPath.
find(
'.', start);
126 if (dot == std::string::npos) {
127 segs.push_back(dottedPath.substr(start));
130 segs.push_back(dottedPath.substr(start, dot - start));
133 if (segs.empty() || (segs.size() == 1 && segs[0].empty()))
return false;
136 for (
size_t si = 0; si + 1 < segs.size(); ++si) {
138 std::vector<size_t> indexes;
141 if (!parseSegment(segs[si], key, indexes) || key.empty() || !indexes.empty())
return false;
146 next = cur->
find(key);
152 std::vector<size_t> indexes;
153 if (!parseSegment(segs.back(), key, indexes))
return false;
154 if (!indexes.empty()) {
158 if (!arr || !arr->
isArray())
return false;
159 cur = cur->
find(key);
161 if (!cur->
isArray() || indexes.size() != 1 || indexes[0] >= cur->arr_.size())
return false;
162 cur->arr_[indexes[0]] = std::move(
v);
165 if (!cur->
isObject() || key.empty())
return false;
166 cur->
set(key, std::move(
v));
172 bool changed =
false;
173 for (
const auto& kv : defaults.obj_) {
176 set(kv.first, kv.second);
178 }
else if (mine->
isObject() && kv.second.isObject()) {
186 if (kind_ != o.kind_)
return false;
JSON-compatible state value tree used by state hot reload.
static StateValue object()
Empty object.
static StateValue boolean(bool v)
Boolean value.
bool setPath(const std::string &dottedPath, StateValue v)
Set a value at a dotted path, creating missing intermediate objects along the way.
static StateValue integer(int64_t v)
Integer value.
bool mergeDefaults(const StateValue &defaults)
Fill missing members from defaults; object members merge recursively, existing values are never overw...
void pushBack(StateValue v)
Append an element; only valid on arrays.
const StateValue * get(const std::string &dottedPath) const
Navigate a dotted path ("a.b[2].c").
bool operator==(const StateValue &o) const
static StateValue number(double v)
Floating-point value.
const StateValue * find(const std::string &key) const
Look up key; nullptr when absent.
std::vector< std::string > keys() const
Member names in insertion order.
static StateValue string(std::string v)
String value.
void set(const std::string &key, StateValue v)
Insert or replace key; only valid on objects.