17 #include "yaml-cpp/yaml.h"
39 std::string_view key) {
41 for (
const auto §ion : node.value()) {
54 if (section.first.Scalar() == key) {
55 node.value().reset(node.value()[key]);
71 std::vector<std::string> to_remove(root.size());
75 if (
n.second.IsMap() &&
n.second.size() == 0) {
76 to_remove.emplace_back(
n.first.Scalar());
79 for (
const auto &key : to_remove) {
93 YAML::Node
operator|=(YAML::Node a,
const YAML::Node &b) {
96 a[n0.first.Scalar()] |= n0.second;
110 std::string
join_quoted(std::initializer_list<const char *> keys) {
111 return std::accumulate(keys.begin(), keys.end(), std::string{
"{"},
112 [](
const std::string &ss,
const std::string &s) {
113 return ss + ((ss.size() == 1) ?
"\"" :
", \"") + s +
127 const std::filesystem::path &filename) {
128 const auto file_path = path / filename;
129 if (!std::filesystem::exists(file_path)) {
132 "', but the file does not exist.");
135 throw std::runtime_error(
136 "The configuration file has CR LF line endings. Please use LF "
140 root_node_ = YAML::LoadFile(file_path.native());
141 }
catch (YAML::ParserException &e) {
142 if (e.msg ==
"illegal map value" || e.msg ==
"end of map not found") {
143 const auto line = std::to_string(e.mark.line + 1);
144 throw ParseError(
"YAML parse error at\n" + file_path.native() +
':' +
145 line +
": " + e.msg +
146 " (check that the indentation of map keys matches)");
153 : root_node_(std::move(other.root_node_)),
154 uncaught_exceptions_(std::move(other.uncaught_exceptions_)) {
155 other.root_node_.reset();
156 other.uncaught_exceptions_ = 0;
164 other.root_node_.reset();
165 other.uncaught_exceptions_ = 0;
175 throw std::logic_error(
176 "Configuration object destroyed with unused keys:\n" +
to_string());
186 }
catch (YAML::ParserException &e) {
187 if (e.msg ==
"illegal map value" || e.msg ==
"end of map not found") {
188 const auto line = std::to_string(e.mark.line + 1);
189 throw ParseError(
"YAML parse error in:\n" + yaml +
"\nat line " + line +
191 " (check that the indentation of map keys matches)");
198 std::vector<std::string> r;
201 r.emplace_back(i.first.Scalar());
207 std::initializer_list<const char *> keys) {
208 assert(keys.size() > 0);
212 auto last_key_it = keys.end() - 1;
214 auto to_be_returned{previous_to_last_node};
216 if (!previous_to_last_node || !to_be_returned) {
217 throw std::invalid_argument(
218 "Attempt to take value of a not existing key: " +
join_quoted(keys));
220 previous_to_last_node.value().remove(*last_key_it);
222 return {to_be_returned.value(), *last_key_it};
226 std::initializer_list<const char *> keys)
const {
229 return {found_node.value(), keys.begin()[keys.size() - 1]};
231 throw std::invalid_argument(
232 "Attempt to read value of a not existing key: " +
join_quoted(keys));
237 const std::string &key, std::initializer_list<const char *> section) {
240 std::vector<std::string> to_remove{};
241 bool key_exists =
false;
242 for (
auto i : found_node.value()) {
243 if (i.first.Scalar() != key) {
244 to_remove.push_back(i.first.Scalar());
250 std::string section_string{
" section "};
251 if (section.size() > 0) {
254 section_string =
" top-level" + section_string;
256 throw std::invalid_argument(
"Attempt to remove all keys in" +
258 "except not existing one: \"" + key +
"\"");
260 for (
auto i : to_remove) {
261 found_node.value().remove(i);
265 throw std::invalid_argument(
266 "Attempt to remove entries in not existing section: " +
272 std::initializer_list<const char *> keys,
275 assert(keys.size() > 0);
276 auto last_key_it = keys.end() - 1;
277 auto previous_to_section_node =
279 auto sub_conf_root_node{previous_to_section_node};
281 if (!previous_to_section_node || !sub_conf_root_node) {
285 throw std::runtime_error(
"Attempt to extract not existing section " +
291 else if (sub_conf_root_node->IsNull() ||
292 (sub_conf_root_node->IsMap() && sub_conf_root_node->size() == 0)) {
295 throw std::runtime_error(
"Attempt to extract empty section " +
297 }
else if (sub_conf_root_node->IsMap() && sub_conf_root_node->size() != 0) {
299 previous_to_section_node->remove(*last_key_it);
303 throw std::runtime_error(
"Tried to extract configuration section at " +
305 " to get a key value. Use take instead!");
310 std::initializer_list<const char *> keys)
const {
312 return found_node.has_value();
317 return found_node.has_value() && !(found_node.value().IsNull());
327 std::vector<const char *> keys)
const {
333 for (
const auto &key : keys) {
340 std::vector<const char *> keys)
const {
341 assert(keys.size() > 0);
343 for (
const auto &key : keys) {
345 node.reset(node[key]);
371 std::vector<KeyLabels> &list,
375 for (
const auto &sub_node : root_node) {
376 new_list_entry.push_back(sub_node.first.as<std::string>());
377 if (sub_node.second.IsMap())
381 list.push_back(new_list_entry);
382 new_list_entry.pop_back();
400 std::vector<KeyLabels> list{};
411 template <
typename T>
418 static constexpr
bool value =
false;
428 template <
typename MapKey,
typename MapValue>
434 static constexpr
bool value =
true;
444 std::vector<KeyLabels> labels_of_keys_taken_as_map{};
447 [&labels_of_keys_taken_as_map](
auto &&var) {
458 if constexpr (
IsStdMap<
typename std::decay_t<
459 decltype(var.get())>::type>::value)
460 labels_of_keys_taken_as_map.push_back(var.get().labels());
464 return labels_of_keys_taken_as_map;
480 std::vector<KeyLabels> &list_of_input_key_labels) {
481 const std::vector<KeyLabels> labels_of_keys_taken_as_map =
483 for (
const auto &labels : labels_of_keys_taken_as_map) {
485 list_of_input_key_labels.end(),
486 [&labels](
KeyLabels &labels_of_input_key) {
487 if (std::equal(labels.begin(), labels.end(),
488 labels_of_input_key.begin(),
489 labels_of_input_key.begin() + labels.size()))
490 labels_of_input_key = labels;
495 list_of_input_key_labels.erase(std::unique(list_of_input_key_labels.begin(),
496 list_of_input_key_labels.end()),
497 list_of_input_key_labels.end());
516 auto key_ref_var_it = std::find_if(
518 [&labels](
auto key) {
520 [&labels](
auto &&arg) {
return arg.get().has_same_labels(labels); },
525 " is not a valid SMASH input key.");
530 const auto key_labels =
531 std::visit([](
auto &&var) {
return static_cast<std::string
>(var.get()); },
534 if (std::visit([](
auto &&var) {
return !var.get().is_allowed(); },
536 const auto v_removal = std::visit(
537 [](
auto &&var) {
return var.get().removed_in(); }, found_variant);
539 " has been removed in version ", v_removal,
540 " and it is not valid anymore.");
543 if (std::visit([](
auto &&var) {
return var.get().is_deprecated(); },
545 const auto v_deprecation = std::visit(
546 [](
auto &&var) {
return var.get().deprecated_in(); }, found_variant);
548 "Key ", key_labels,
" has been deprecated in version ", v_deprecation);
569 switch (result_so_far) {
574 result_so_far = new_value;
578 result_so_far = new_value;
589 for (
const auto &key_labels : list) {
591 if (full_validation) {
598 return validation_result;
Return type of Configuration::take that automatically determines the target type.
Interface to the SMASH configuration files.
Is
Return type of Configuration::validate which conveys more information that simply a two-state boolean...
bool has_value(std::initializer_list< const char * > keys) const
Return whether there is a non-empty value behind the requested keys.
void merge_yaml(const std::string &yaml)
Merge the configuration in yaml into the existing tree.
std::string to_string() const
Return a string of the current YAML tree.
bool has_value_including_empty(std::initializer_list< const char * > keys) const
Return whether there is a (maybe empty) value behind the requested keys.
Configuration(const std::filesystem::path &path)
Read config.yaml from the specified path.
void remove_all_entries_in_section_but_one(const std::string &key, std::initializer_list< const char * > section={})
Remove all entries in the given section except for key.
YAML::Node find_node_creating_it_if_not_existing(std::vector< const char * > keys) const
Descend in and if needed modify the YAML tree from the given node using the provided keys.
YAML::Node root_node_
The general_config.yaml contents - fully parsed.
Configuration extract_sub_configuration(std::initializer_list< const char * > keys, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Create a new configuration from a then-removed section of the present object.
int uncaught_exceptions_
Counter to be able to optionally throw in destructor.
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
Value read(std::initializer_list< const char * > keys) const
Additional interface for SMASH to read configuration values without removing them.
Is validate(bool full_validation=true) const
Validate content of configuration in terms of YAML keys.
~Configuration() noexcept(false)
Destroy the object, optionally throwing if not all keys were taken.
GetEmpty
Flag to tune method(s) behavior such that it is descriptive from the caller side.
std::optional< YAML::Node > find_existing_node(std::vector< const char * > keys) const
Descend in the YAML tree from the given node using the provided keys.
std::vector< std::string > list_upmost_nodes()
Lists all YAML::Nodes from the configuration setup.
Configuration & operator=(const Configuration &)=delete
Prevent Configuration objects from being copy-assigned.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
auto collect_input_keys_taken_as_maps()
Extract from the InputKeys database the labels of keys that have a std::map as type.
void adjust_list_of_labels_dealing_with_keys_taken_as_maps(std::vector< KeyLabels > &list_of_input_key_labels)
Remove last labels of keys that are taken as maps in SMASH and remove duplicates from the resulting l...
void fill_list_of_labels_per_key_in_yaml_tree(const YAML::Node &root_node, std::vector< KeyLabels > &list, KeyLabels &new_list_entry)
Implementation of the algorithm to translate a YAML tree into lists of labels, each identifying a key...
YAML::Node operator|=(YAML::Node a, const YAML::Node &b)
Merge two YAML::Nodes.
void accumulate_validation(Configuration::Is &result_so_far, Configuration::Is new_value)
Utility function to accumulate validation results of keys.
Configuration::Is validate_key(const KeyLabels &labels)
Given some YAML labels (assumed to be in order from the top section), it is checked whether any valid...
void descend_one_existing_level(std::optional< YAML::Node > &node, std::string_view key)
Reset the passed in node to that one at the provided key, which is expected to exist in the node.
std::string join_quoted(std::initializer_list< const char * > keys)
Build a string with a list of keys as specified in the code.
YAML::Node remove_empty_maps(YAML::Node root)
Remove all empty maps of a YAML::Node.
auto get_list_of_labels_per_key_in_yaml_tree(const YAML::Node &root_node)
Create a list of lists of key labels present in the passed YAML node considered to be the root one of...
std::vector< std::string > KeyLabels
Descriptive alias for storing key labels, i.e.
UnaryFunction for_each(Container &&c, UnaryFunction &&f)
Convenience wrapper for std::for_each that operates on a complete container.
std::string quote(const std::string &s)
Add quotes around string.
static constexpr int LConfiguration
std::string join(const std::vector< std::string > &v, const std::string &delim)
Join strings using delimiter.
bool has_crlf_line_ending(const std::string in)
Check if a line in the string ends with \r\n.
std::string read_all(std::istream &&input)
Utility function to read a complete input stream (e.g.
Thrown if the file does not exist.
Thrown for YAML parse errors.
std::variant< std::reference_wrapper< const Key< bool > >, std::reference_wrapper< const Key< int > >, std::reference_wrapper< const Key< double > >, std::reference_wrapper< const Key< std::string > >, std::reference_wrapper< const Key< std::array< int, 3 > >>, std::reference_wrapper< const Key< std::array< double, 2 > >>, std::reference_wrapper< const Key< std::array< double, 3 > >>, std::reference_wrapper< const Key< std::pair< double, double > >>, std::reference_wrapper< const Key< std::vector< double > >>, std::reference_wrapper< const Key< std::vector< std::string > >>, std::reference_wrapper< const Key< std::set< ThermodynamicQuantity > >>, std::reference_wrapper< const Key< std::map< PdgCode, int > >>, std::reference_wrapper< const Key< std::map< std::string, std::string > >>, std::reference_wrapper< const Key< einhard::LogLevel > >, std::reference_wrapper< const Key< BoxInitialCondition > >, std::reference_wrapper< const Key< CalculationFrame > >, std::reference_wrapper< const Key< CollisionCriterion > >, std::reference_wrapper< const Key< DensityType > >, std::reference_wrapper< const Key< DerivativesMode > >, std::reference_wrapper< const Key< ExpansionMode > >, std::reference_wrapper< const Key< FermiMotion > >, std::reference_wrapper< const Key< FieldDerivativesMode > >, std::reference_wrapper< const Key< MultiParticleReactionsBitSet > >, std::reference_wrapper< const Key< NNbarTreatment > >, std::reference_wrapper< const Key< OutputOnlyFinal > >, std::reference_wrapper< const Key< PdgCode > >, std::reference_wrapper< const Key< PseudoResonance > >, std::reference_wrapper< const Key< ReactionsBitSet > >, std::reference_wrapper< const Key< RestFrameDensityDerivativesMode > >, std::reference_wrapper< const Key< Sampling > >, std::reference_wrapper< const Key< SmearingMode > >, std::reference_wrapper< const Key< SphereInitialCondition > >, std::reference_wrapper< const Key< ThermalizationAlgorithm > >, std::reference_wrapper< const Key< TimeStepMode > >, std::reference_wrapper< const Key< TotalCrossSectionStrategy > >> key_references_variant
Alias for the type to be used in the list of keys.
A utility type to be specialized to check if a type is a std::map .