1+ #include < algorithm>
2+ #include < cctype>
3+ #include < cstdlib>
4+ #include < optional>
15#include < boost/algorithm/string.hpp>
26#include < rime/common.h>
37#include < rime/resource.h>
@@ -101,7 +105,9 @@ static bool AppendToString(an<ConfigItemRef> target, an<ConfigValue> value) {
101105 return true ;
102106}
103107
104- static bool AppendToList (an<ConfigItemRef> target, an<ConfigList> list) {
108+ static bool AppendToList (an<ConfigItemRef> target,
109+ an<ConfigList> list,
110+ std::optional<size_t > insert_pos = std::nullopt ) {
105111 if (!list)
106112 return false ;
107113 auto existing_list = As<ConfigList>(**target);
@@ -117,9 +123,20 @@ static bool AppendToList(an<ConfigItemRef> target, an<ConfigList> list) {
117123 if (list->empty ())
118124 return true ;
119125 auto copy = New<ConfigList>(*existing_list);
126+ if (insert_pos && *insert_pos > copy->size ()) {
127+ LOG (ERROR ) << " list insert position out of range: " << *insert_pos;
128+ return false ;
129+ }
130+ size_t current_index = insert_pos.value_or (copy->size ());
120131 for (ConfigList::Iterator iter = list->begin (); iter != list->end (); ++iter) {
121- if (!copy->Append (*iter))
122- return false ;
132+ if (insert_pos) {
133+ if (!copy->Insert (current_index, *iter))
134+ return false ;
135+ ++current_index;
136+ } else {
137+ if (!copy->Append (*iter))
138+ return false ;
139+ }
123140 }
124141 *target = copy;
125142 return true ;
@@ -148,25 +165,63 @@ static bool MergeTree(an<ConfigItemRef> target, an<ConfigMap> map) {
148165static constexpr const char * ADD_SUFFIX_OPERATOR = " /+" ;
149166static constexpr const char * EQU_SUFFIX_OPERATOR = " /=" ;
150167
151- inline static bool IsAppending (const string& key) {
168+ static std::optional<size_t > ParseIndexedAppend (const string& key) {
169+ if (key.empty () || key.back () != ' +' )
170+ return std::nullopt ;
171+ size_t plus_pos = key.size () - 1 ;
172+ size_t digit_begin = plus_pos;
173+ while (digit_begin > 0 &&
174+ std::isdigit (static_cast <unsigned char >(key[digit_begin - 1 ]))) {
175+ --digit_begin;
176+ }
177+ if (digit_begin == plus_pos)
178+ return std::nullopt ;
179+ if (digit_begin > 0 && key[digit_begin - 1 ] != ' /' )
180+ return std::nullopt ;
181+ string index_part = key.substr (digit_begin, plus_pos - digit_begin);
182+ if (index_part.empty ())
183+ return std::nullopt ;
184+ return static_cast <size_t >(std::strtoull (index_part.c_str (), nullptr , 10 ));
185+ }
186+
187+ inline static bool IsAppending (const string& key,
188+ const std::optional<size_t >& indexed_append) {
152189 return key == ConfigCompiler::APPEND_DIRECTIVE ||
153- boost::ends_with (key, ADD_SUFFIX_OPERATOR );
190+ boost::ends_with (key, ADD_SUFFIX_OPERATOR ) ||
191+ indexed_append.has_value ();
154192}
155193inline static bool IsMerging (const string& key,
156194 const an<ConfigItem>& value,
157- bool merge_tree) {
158- return key == ConfigCompiler::MERGE_DIRECTIVE ||
159- boost::ends_with (key, ADD_SUFFIX_OPERATOR ) ||
195+ bool merge_tree,
196+ const std::optional<size_t >& indexed_append) {
197+ bool has_plain_add_suffix =
198+ boost::ends_with (key, ADD_SUFFIX_OPERATOR ) && !indexed_append.has_value ();
199+ return key == ConfigCompiler::MERGE_DIRECTIVE || has_plain_add_suffix ||
160200 (merge_tree && (!value || Is<ConfigMap>(value)) &&
161201 !boost::ends_with (key, EQU_SUFFIX_OPERATOR ));
162202}
163203
164- inline static string StripOperator (const string& key, bool adding) {
165- return (key == ConfigCompiler::APPEND_DIRECTIVE ||
166- key == ConfigCompiler::MERGE_DIRECTIVE )
167- ? " "
168- : boost::erase_last_copy (
169- key, adding ? ADD_SUFFIX_OPERATOR : EQU_SUFFIX_OPERATOR );
204+ inline static string StripOperator (
205+ const string& key,
206+ bool adding,
207+ const std::optional<size_t >& indexed_append) {
208+ if (key == ConfigCompiler::APPEND_DIRECTIVE ||
209+ key == ConfigCompiler::MERGE_DIRECTIVE ) {
210+ return " " ;
211+ }
212+ if (indexed_append) {
213+ auto suffix_with_slash =
214+ string (" /" ) + std::to_string (*indexed_append) + " +" ;
215+ if (boost::ends_with (key, suffix_with_slash)) {
216+ return key.substr (0 , key.size () - suffix_with_slash.size ());
217+ }
218+ auto suffix_plain = std::to_string (*indexed_append) + " +" ;
219+ if (boost::ends_with (key, suffix_plain)) {
220+ return key.substr (0 , key.size () - suffix_plain.size ());
221+ }
222+ }
223+ return boost::erase_last_copy (
224+ key, adding ? ADD_SUFFIX_OPERATOR : EQU_SUFFIX_OPERATOR );
170225}
171226
172227// defined in config_data.cc
@@ -180,9 +235,10 @@ static bool EditNode(an<ConfigItemRef> head,
180235 const an<ConfigItem>& value,
181236 bool merge_tree) {
182237 DLOG (INFO ) << " edit node: " << key << " , merge_tree: " << merge_tree;
183- bool appending = IsAppending (key);
184- bool merging = IsMerging (key, value, merge_tree);
185- string path = StripOperator (key, appending || merging);
238+ auto indexed_append = ParseIndexedAppend (key);
239+ bool appending = IsAppending (key, indexed_append);
240+ bool merging = IsMerging (key, value, merge_tree, indexed_append);
241+ string path = StripOperator (key, appending || merging, indexed_append);
186242 DLOG (INFO ) << " appending: " << appending << " , merging: " << merging
187243 << " , path: " << path;
188244 auto find_target_node =
@@ -195,8 +251,9 @@ static bool EditNode(an<ConfigItemRef> head,
195251 if ((appending || merging) && **target) {
196252 DLOG (INFO ) << " writer: editing node" ;
197253 return !value || // no-op
198- (appending && (AppendToString (target, As<ConfigValue>(value)) ||
199- AppendToList (target, As<ConfigList>(value)))) ||
254+ (appending &&
255+ (AppendToString (target, As<ConfigValue>(value)) ||
256+ AppendToList (target, As<ConfigList>(value), indexed_append))) ||
200257 (merging && MergeTree (target, As<ConfigMap>(value)));
201258 } else {
202259 DLOG (INFO ) << " writer: overwriting node" ;
0 commit comments