@@ -91,7 +91,7 @@ merge_new_elements(ConfigManager::arglist_t& to, const ConfigManager::arglist_t&
91
91
return p.first == v.first ;
92
92
});
93
93
94
- if (it == to.end ())
94
+ if (it == to.end () || p. first == " metadata " ) // hacky but we want to allow multiple entries for metadata
95
95
to.push_back (p);
96
96
}
97
97
@@ -180,6 +180,152 @@ expand_variables(const std::string& in, const std::string& val)
180
180
return ret;
181
181
}
182
182
183
+ ConfigManager::arglist_t
184
+ parse_keyval_list (std::istream& is)
185
+ {
186
+ ConfigManager::arglist_t ret;
187
+ char c = 0 ;
188
+
189
+ do {
190
+ std::string key = util::read_word (is, " =" );
191
+ if (key.empty ())
192
+ return ret;
193
+
194
+ c = util::read_char (is);
195
+ if (c != ' =' )
196
+ return ret;
197
+
198
+ std::string val = util::read_word (is, " ,()" );
199
+ if (!val.empty ())
200
+ ret.push_back (std::make_pair (key, val));
201
+
202
+ c = util::read_char (is);
203
+ } while (is.good () && c == ' ,' );
204
+
205
+ if (is.good ())
206
+ is.unget ();
207
+
208
+ return ret;
209
+ }
210
+
211
+ std::pair<bool , StringConverter>
212
+ find_key_in_json (const std::vector<std::string>& path, const std::map<std::string, StringConverter>& dict)
213
+ {
214
+ if (path.empty ())
215
+ return std::make_pair (false , StringConverter ());
216
+
217
+ auto it = dict.find (path.front ());
218
+ if (it == dict.end ())
219
+ return std::make_pair (false , StringConverter ());
220
+
221
+ if (path.size () == 1 )
222
+ return std::make_pair (true , it->second );
223
+
224
+ StringConverter ret = it->second ;
225
+ for (auto path_it = path.begin ()+1 ; path_it != path.end (); ++path_it) {
226
+ auto sub_dict = ret.rec_dict ();
227
+ it = sub_dict.find (*path_it);
228
+ if (it == sub_dict.end ())
229
+ return std::make_pair (false , StringConverter ());
230
+ ret = it->second ;
231
+ }
232
+
233
+ return std::make_pair (true , ret);
234
+ }
235
+
236
+ unsigned
237
+ add_metadata_entries (const std::string& key, const StringConverter& val, info_map_t & info)
238
+ {
239
+ unsigned num_entries = 0 ;
240
+
241
+ bool is_dict = false ;
242
+ auto dict = val.rec_dict (&is_dict);
243
+
244
+ if (is_dict) {
245
+ std::string prefix = key + " ." ;
246
+ for (const auto & p : dict) {
247
+ info[prefix + p.first ] = p.second .to_string ();
248
+ ++num_entries;
249
+ }
250
+ } else {
251
+ info[key] = val.to_string ();
252
+ ++num_entries;
253
+ }
254
+
255
+ return num_entries;
256
+ }
257
+
258
+ void
259
+ read_metadata_from_json_file (const std::string& filename, const std::string& keys, info_map_t & info)
260
+ {
261
+ std::ifstream is (filename.c_str (), std::ios::ate);
262
+
263
+ if (!is) {
264
+ Log (0 ).stream () << " read_metadata_from_json_file(): Cannot open file "
265
+ << filename << " , quitting\n " ;
266
+ return ;
267
+ }
268
+
269
+ auto size = is.tellg ();
270
+ std::string str (size, ' \0 ' );
271
+ is.seekg (0 );
272
+ if (!is.read (&str[0 ], size)) {
273
+ Log (0 ).stream () << " read_metadata_from_json_file(): Cannot read file "
274
+ << filename << " , quitting\n " ;
275
+ return ;
276
+ }
277
+
278
+ bool ok = false ;
279
+ auto top = StringConverter (str).rec_dict (&ok);
280
+
281
+ if (!ok) {
282
+ Log (0 ).stream () << " read_metadata_from_json_file(): Cannot parse top-level dict in "
283
+ << filename << " , quitting\n " ;
284
+ return ;
285
+ }
286
+
287
+ auto keylist = StringConverter (keys).to_stringlist ();
288
+ if (keylist.empty ()) {
289
+ for (const auto & p : top)
290
+ keylist.push_back (p.first );
291
+ }
292
+
293
+ for (const std::string& key : keylist) {
294
+ std::vector<std::string> path = StringConverter (key).to_stringlist (" ." );
295
+ auto ret = find_key_in_json (path, top);
296
+ if (ret.first )
297
+ add_metadata_entries (key, ret.second , info);
298
+ else
299
+ Log (1 ).stream () << " read_metadata_from_json_file(): Key "
300
+ << key << " not found\n " ;
301
+ }
302
+ }
303
+
304
+ void
305
+ add_metadata (const std::string& args, info_map_t & info)
306
+ {
307
+ std::istringstream is (args);
308
+ auto arglist = parse_keyval_list (is);
309
+
310
+ auto it = std::find_if (arglist.begin (), arglist.end (), [](const std::pair<std::string,std::string>& p){
311
+ return p.first == " file" ;
312
+ });
313
+
314
+ if (it != arglist.end ()) {
315
+ std::string filename = it->second ;
316
+ std::string keys;
317
+ it = std::find_if (arglist.begin (), arglist.end (), [](const std::pair<std::string,std::string>& p){
318
+ return p.first == " keys" ;
319
+ });
320
+ if (it != arglist.end ())
321
+ keys = it->second ;
322
+ read_metadata_from_json_file (filename, keys, info);
323
+ } else {
324
+ for (const auto & p : arglist)
325
+ info[p.first ] = p.second ;
326
+ }
327
+ }
328
+
183
329
} // namespace [anonymous]
184
330
185
331
@@ -540,8 +686,15 @@ struct ConfigManager::Options::OptionsImpl
540
686
541
687
void
542
688
update_channel_metadata (info_map_t & info) {
543
- for (const auto &p : args)
544
- info[p.first ] = p.second ;
689
+ for (const auto &p : args) {
690
+ if (p.first == " metadata" ) {
691
+ ::add_metadata (p.second, info);
692
+ } else {
693
+ std::string key = " opts:" ;
694
+ key.append (p.first );
695
+ info[key] = p.second ;
696
+ }
697
+ }
545
698
}
546
699
547
700
std::string
@@ -614,19 +767,16 @@ struct ConfigManager::Options::OptionsImpl
614
767
615
768
for (const auto &argp : args) {
616
769
auto s_it = spec.data .find (argp.first );
617
-
618
770
if (s_it == spec.data .end ())
619
771
continue ;
620
772
621
773
// Non-boolean options are enabled if they are present in args.
622
774
// For boolean options, check if they are set to false or true.
623
775
bool enabled = true ;
624
-
625
776
if (s_it->second .type == " bool" )
626
777
enabled = StringConverter (argp.second ).to_bool ();
627
778
if (enabled) {
628
779
vec.push_back (argp.first );
629
-
630
780
auto tmp = get_inherited_specs (argp.first );
631
781
vec.insert (vec.end (), tmp.begin (), tmp.end ());
632
782
}
@@ -745,8 +895,8 @@ struct ConfigManager::ConfigManagerImpl
745
895
std::map< std::string, arglist_t >
746
896
m_default_parameters_for_spec;
747
897
748
- arglist_t m_default_parameters;
749
- argmap_t m_extra_vars;
898
+ arglist_t m_default_parameters;
899
+ argmap_t m_extra_vars;
750
900
751
901
OptionSpec m_global_opts;
752
902
@@ -893,7 +1043,7 @@ struct ConfigManager::ConfigManagerImpl
893
1043
add_config_spec (s.spec , s.create , s.check_args , true /* ignore existing */ );
894
1044
}
895
1045
896
- // Parse "=value"
1046
+ // Parse "=value" or "(value)"
897
1047
// Returns an empty string if there is no '=', otherwise the string after '='.
898
1048
// Sets error if there is a '=' but no word afterwards.
899
1049
std::string
@@ -906,8 +1056,12 @@ struct ConfigManager::ConfigManagerImpl
906
1056
907
1057
if (val.empty ())
908
1058
set_error (" Expected value after \" " + key + " =\" " , is);
909
- }
910
- else
1059
+ } else if (c == ' (' ) {
1060
+ val = util::read_nested_text (is, ' (' , ' )' );
1061
+ c = util::read_char (is);
1062
+ if (c != ' )' )
1063
+ set_error (" Missing ')' after \" " + key + " \" (" , is);
1064
+ } else
911
1065
is.unget ();
912
1066
913
1067
return val;
@@ -931,7 +1085,7 @@ struct ConfigManager::ConfigManagerImpl
931
1085
std::string key = util::read_word (is, " ,=()\n " );
932
1086
933
1087
if (!key.empty ()) {
934
- if (!(opts.contains (key))) {
1088
+ if (!(opts.contains (key)) && key != " metadata " ) {
935
1089
set_error (" Unknown option: " + key);
936
1090
args.clear ();
937
1091
return args;
@@ -1121,7 +1275,9 @@ struct ConfigManager::ConfigManagerImpl
1121
1275
if (m_error)
1122
1276
return ret;
1123
1277
1124
- if (is_option (key)) {
1278
+ if (key == " metadata" ) {
1279
+ m_default_parameters.push_back (std::make_pair (key, val));
1280
+ } else if (is_option (key)) {
1125
1281
if (val.empty ())
1126
1282
val = " true" ;
1127
1283
0 commit comments