@@ -127,13 +127,21 @@ bool PendingChild::Resolve(ConfigCompiler* compiler) {
127
127
static an<ConfigItem> ResolveReference (ConfigCompiler* compiler,
128
128
const Reference& reference);
129
129
130
+ static bool MergeTree (an<ConfigItemRef> target, an<ConfigMap> map);
131
+
130
132
bool IncludeReference::Resolve (ConfigCompiler* compiler) {
131
133
DLOG (INFO) << " IncludeReference::Resolve(reference = " << reference << " )" ;
132
- auto item = ResolveReference (compiler, reference);
133
- if (!item ) {
134
+ auto included = ResolveReference (compiler, reference);
135
+ if (!included ) {
134
136
return reference.optional ;
135
137
}
136
- *target = item;
138
+ // merge literal key-values into the included map
139
+ auto overrides = As<ConfigMap>(**target);
140
+ *target = included;
141
+ if (overrides && !overrides->empty () && !MergeTree (target, overrides)) {
142
+ LOG (ERROR) << " failed to merge tree: " << reference;
143
+ return false ;
144
+ }
137
145
return true ;
138
146
}
139
147
@@ -152,19 +160,119 @@ bool PatchReference::Resolve(ConfigCompiler* compiler) {
152
160
return patch.Resolve (compiler);
153
161
}
154
162
163
+ static bool AppendToString (an<ConfigItemRef> target, an<ConfigValue> value) {
164
+ if (!value)
165
+ return false ;
166
+ auto existing_value = As<ConfigValue>(**target);
167
+ if (!existing_value) {
168
+ LOG (ERROR) << " trying to append string to non scalar" ;
169
+ return false ;
170
+ }
171
+ *target = existing_value->str () + value->str ();
172
+ return true ;
173
+ }
174
+
175
+ static bool AppendToList (an<ConfigItemRef> target, an<ConfigList> list) {
176
+ if (!list)
177
+ return false ;
178
+ auto existing_list = As<ConfigList>(**target);
179
+ if (!existing_list) {
180
+ LOG (ERROR) << " trying to append list to other value" ;
181
+ return false ;
182
+ }
183
+ if (list->empty ())
184
+ return true ;
185
+ auto copy = New<ConfigList>(*existing_list);
186
+ for (ConfigList::Iterator iter = list->begin (); iter != list->end (); ++iter) {
187
+ if (!copy->Append (*iter))
188
+ return false ;
189
+ }
190
+ *target = copy;
191
+ return true ;
192
+ }
193
+
194
+ static bool EditNode (an<ConfigItemRef> target,
195
+ const string& key,
196
+ const an<ConfigItem>& value,
197
+ bool merge_tree);
198
+
199
+ static bool MergeTree (an<ConfigItemRef> target, an<ConfigMap> map) {
200
+ if (!map)
201
+ return false ;
202
+ // NOTE: the referenced content of target can be any type
203
+ for (ConfigMap::Iterator iter = map->begin (); iter != map->end (); ++iter) {
204
+ const auto & key = iter->first ;
205
+ const auto & value = iter->second ;
206
+ if (!EditNode (target, key, value, true )) {
207
+ LOG (ERROR) << " error merging branch " << key;
208
+ return false ;
209
+ }
210
+ }
211
+ return true ;
212
+ }
213
+
214
+ static constexpr const char * ADD_SUFFIX_OPERATOR = " /+" ;
215
+ static constexpr const char * EQU_SUFFIX_OPERATOR = " /=" ;
216
+
217
+ inline static bool IsAppending (const string& key) {
218
+ return key == ConfigCompiler::APPEND_DIRECTIVE ||
219
+ boost::ends_with (key, ADD_SUFFIX_OPERATOR);
220
+ }
221
+ inline static bool IsMerging (const string& key,
222
+ const an<ConfigItem>& value,
223
+ bool merge_tree) {
224
+ return key == ConfigCompiler::MERGE_DIRECTIVE ||
225
+ boost::ends_with (key, ADD_SUFFIX_OPERATOR) ||
226
+ (merge_tree && Is<ConfigMap>(value) &&
227
+ !boost::ends_with (key, EQU_SUFFIX_OPERATOR));
228
+ }
229
+
230
+ inline static string StripOperator (const string& key, bool adding) {
231
+ return (key == ConfigCompiler::APPEND_DIRECTIVE ||
232
+ key == ConfigCompiler::MERGE_DIRECTIVE) ? " " :
233
+ boost::erase_last_copy (
234
+ key, adding ? ADD_SUFFIX_OPERATOR : EQU_SUFFIX_OPERATOR);
235
+ }
236
+
155
237
// defined in config_data.cc
156
238
bool TraverseCopyOnWrite (an<ConfigItemRef> root, const string& path,
157
- an<ConfigItem> item);
239
+ function<bool (an<ConfigItemRef> target)> writer);
240
+
241
+ static bool EditNode (an<ConfigItemRef> target,
242
+ const string& key,
243
+ const an<ConfigItem>& value,
244
+ bool merge_tree) {
245
+ DLOG (INFO) << " EditNode(" << key << " ," << merge_tree << " )" ;
246
+ bool appending = IsAppending (key);
247
+ bool merging = IsMerging (key, value, merge_tree);
248
+ auto writer = [=](an<ConfigItemRef> target) {
249
+ if ((appending || merging) && **target) {
250
+ DLOG (INFO) << " writer: editing node" ;
251
+ return !value ||
252
+ (appending && (AppendToString (target, As<ConfigValue>(value)) ||
253
+ AppendToList (target, As<ConfigList>(value)))) ||
254
+ (merging && MergeTree (target, As<ConfigMap>(value)));
255
+ } else {
256
+ DLOG (INFO) << " writer: overwriting node" ;
257
+ *target = value;
258
+ return true ;
259
+ }
260
+ };
261
+ string path = StripOperator (key, appending || merging);
262
+ DLOG (INFO) << " appending: " << appending << " , merging: " << merging
263
+ << " , path: " << path;
264
+ return TraverseCopyOnWrite (target, path, writer);
265
+ }
158
266
159
267
bool PatchLiteral::Resolve (ConfigCompiler* compiler) {
160
268
DLOG (INFO) << " PatchLiteral::Resolve()" ;
161
269
bool success = true ;
162
270
for (const auto & entry : *patch) {
163
- const auto & path = entry.first ;
271
+ const auto & key = entry.first ;
164
272
const auto & value = entry.second ;
165
- LOG (INFO) << " patching " << path ;
166
- if (!TraverseCopyOnWrite (target, path , value)) {
167
- LOG (ERROR) << " error applying patch to " << path ;
273
+ LOG (INFO) << " patching " << key ;
274
+ if (!EditNode (target, key , value, false )) {
275
+ LOG (ERROR) << " error applying patch to " << key ;
168
276
success = false ;
169
277
}
170
278
}
0 commit comments