Skip to content

Commit 831ffba

Browse files
kionzlotem
authored andcommittedMar 7, 2018
fix(config_compiler): "/" mistaken as path separator in merged map key (#192)
Fixes #190
1 parent 3cbc9cb commit 831ffba

File tree

2 files changed

+53
-32
lines changed

2 files changed

+53
-32
lines changed
 

‎src/rime/config/config_compiler.cc

+24-18
Original file line numberDiff line numberDiff line change
@@ -155,33 +155,39 @@ inline static string StripOperator(const string& key, bool adding) {
155155
}
156156

157157
// defined in config_data.cc
158-
bool TraverseCopyOnWrite(an<ConfigItemRef> root, const string& path,
159-
function<bool (an<ConfigItemRef> target)> writer);
158+
an<ConfigItemRef> TypeCheckedCopyOnWrite(an<ConfigItemRef> parent,
159+
const string& key);
160+
an<ConfigItemRef> TraverseCopyOnWrite(an<ConfigItemRef> head,
161+
const string& path);
160162

161-
static bool EditNode(an<ConfigItemRef> target,
163+
static bool EditNode(an<ConfigItemRef> head,
162164
const string& key,
163165
const an<ConfigItem>& value,
164166
bool merge_tree) {
165-
DLOG(INFO) << "EditNode(" << key << "," << merge_tree << ")";
167+
DLOG(INFO) << "edit node: " << key << ", merge_tree: " << merge_tree;
166168
bool appending = IsAppending(key);
167169
bool merging = IsMerging(key, value, merge_tree);
168-
auto writer = [=](an<ConfigItemRef> target) {
169-
if ((appending || merging) && **target) {
170-
DLOG(INFO) << "writer: editing node";
171-
return !value ||
172-
(appending && (AppendToString(target, As<ConfigValue>(value)) ||
173-
AppendToList(target, As<ConfigList>(value)))) ||
174-
(merging && MergeTree(target, As<ConfigMap>(value)));
175-
} else {
176-
DLOG(INFO) << "writer: overwriting node";
177-
*target = value;
178-
return true;
179-
}
180-
};
181170
string path = StripOperator(key, appending || merging);
182171
DLOG(INFO) << "appending: " << appending << ", merging: " << merging
183172
<< ", path: " << path;
184-
return TraverseCopyOnWrite(target, path, writer);
173+
auto find_target_node =
174+
merge_tree ? &TypeCheckedCopyOnWrite : &TraverseCopyOnWrite;
175+
auto target = find_target_node(head, path);
176+
if (!target) {
177+
// error finding target node; cannot write
178+
return false;
179+
}
180+
if ((appending || merging) && **target) {
181+
DLOG(INFO) << "writer: editing node";
182+
return !value || // no-op
183+
(appending && (AppendToString(target, As<ConfigValue>(value)) ||
184+
AppendToList(target, As<ConfigList>(value)))) ||
185+
(merging && MergeTree(target, As<ConfigMap>(value)));
186+
} else {
187+
DLOG(INFO) << "writer: overwriting node";
188+
*target = value;
189+
return true;
190+
}
185191
}
186192

187193
bool PatchLiteral::Resolve(ConfigCompiler* compiler) {

‎src/rime/config/config_data.cc

+29-14
Original file line numberDiff line numberDiff line change
@@ -159,37 +159,52 @@ class ConfigDataRootRef : public ConfigItemRef {
159159
ConfigData* data_;
160160
};
161161

162-
bool TraverseCopyOnWrite(an<ConfigItemRef> root, const string& path,
163-
function<bool (an<ConfigItemRef> target)> writer) {
162+
an<ConfigItemRef> TypeCheckedCopyOnWrite(an<ConfigItemRef> parent,
163+
const string& key) {
164+
// special case to allow editing current node by __append: __merge: /+: /=:
165+
if (key.empty()) {
166+
return parent;
167+
}
168+
bool is_list = ConfigData::IsListItemReference(key);
169+
auto expected_node_type = is_list ? ConfigItem::kList : ConfigItem::kMap;
170+
an<ConfigItem> existing_node = *parent;
171+
if (existing_node && existing_node->type() != expected_node_type) {
172+
LOG(ERROR) << "copy on write failed; incompatible node type: " << key;
173+
return nullptr;
174+
}
175+
return Cow(parent, key);
176+
}
177+
178+
an<ConfigItemRef> TraverseCopyOnWrite(an<ConfigItemRef> head,
179+
const string& path) {
164180
DLOG(INFO) << "TraverseCopyOnWrite(" << path << ")";
165181
if (path.empty() || path == "/") {
166-
return writer(root);
182+
return head;
167183
}
168-
an<ConfigItemRef> head = root;
169184
vector<string> keys = ConfigData::SplitPath(path);
170185
size_t n = keys.size();
171186
for (size_t i = 0; i < n; ++i) {
172187
const auto& key = keys[i];
173-
bool is_list = ConfigData::IsListItemReference(key);
174-
auto expected_node_type = is_list ? ConfigItem::kList : ConfigItem::kMap;
175-
an<ConfigItem> existing_node = *head;
176-
if (existing_node && existing_node->type() != expected_node_type) {
177-
LOG(ERROR) << "copy on write failed; incompatible node type: " << key;
178-
return false;
188+
if (auto child = TypeCheckedCopyOnWrite(head, key)) {
189+
head = child;
190+
} else {
191+
LOG(ERROR) << "while writing to " << path;
192+
return nullptr;
179193
}
180-
head = Cow(head, key);
181194
}
182-
return writer(head);
195+
return head;
183196
}
184197

185198
bool ConfigData::TraverseWrite(const string& path, an<ConfigItem> item) {
186199
LOG(INFO) << "write: " << path;
187200
auto root = New<ConfigDataRootRef>(this);
188-
return TraverseCopyOnWrite(root, path, [=](an<ConfigItemRef> target) {
201+
if (auto target = TraverseCopyOnWrite(root, path)) {
189202
*target = item;
190203
set_modified();
191204
return true;
192-
});
205+
} else {
206+
return false;
207+
}
193208
}
194209

195210
vector<string> ConfigData::SplitPath(const string& path) {

0 commit comments

Comments
 (0)
Please sign in to comment.