.. _program_listing_file_lib_options_options.cpp: Program Listing for File options.cpp ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``lib/options/options.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // --------------------------------------------------------------------- // This file is part of falcon-core. // // Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders // // Falcon-server is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Falcon-server is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with falcon-core. If not, see . // --------------------------------------------------------------------- #include #include "options.hpp" using namespace options; bool options::get_nested_yaml_node(const YAML::Node &root, const std::vector &path, YAML::Node &out) { out.reset(root); for (auto &p : path) { if (out[p]) { out.reset(out[p]); } else { return false; } } return true; } void options::set_nested_yaml_node(YAML::Node &root, const std::vector &path, const YAML::Node &value) { YAML::Node x; if (path.size() == 0) { // do nothing } else if (path.size() == 1) { root[path[0]] = value; } else { x.reset(root); for (auto it = path.begin(); it != path.end(); ++it) { if (std::next(it) == path.end()) { x[*it] = value; } else if (!x[*it]) { x[*it] = YAML::Node(YAML::NodeType::Map); } x.reset(x[*it]); } } } OptionBase::OptionBase(std::string name, ValueBase &value, std::string description, bool required) : name_(name), description_(description), required_(required), value_(value) { if (name.size() == 0) { throw std::runtime_error("Option name cannot be empty."); } path_ = split(name, '/'); } std::string OptionBase::name() const { return name_; } std::string OptionBase::description() const { return description_; } const std::vector &OptionBase::path() const { return path_; } bool OptionBase::is_required() const { return required_; } void OptionBase::from_yaml(const YAML::Node &node) { value_.from_yaml(node); } typename YAML::Node OptionBase::to_yaml() const { YAML::Node node; node = value_.to_yaml(); return node; } OptionBase &OptionBase::required() { required_ = true; return *(this); } OptionBase &OptionBase::optional() { required_ = false; return *(this); } OptionBase &OptionBase::describe(std::string description) { description_ = description; return *(this); } OptionBase &OptionBase::set_null() { value_.set_null(); return *(this); } bool OptionBase::is_null() const { return value_.is_null(); } bool OptionBase::is_nullable() const { return value_.is_nullable(); } OptionBase &OptionList::operator[](std::string key) { for (auto &option : options_) { if (option.name() == key) { return option; } } throw std::runtime_error("No such option."); } void OptionList::remove(std::string key) { options_.remove_if([key](const OptionBase &x) { return x.name() == key; }); } std::vector OptionList::options() const { std::vector opts; opts.reserve(options_.size()); for (auto &option : options_) { opts.push_back(option.name()); } return opts; } std::vector OptionList::required_options() const { std::vector opts; for (auto &option : options_) { if (option.is_required()) { opts.push_back(option.name()); } } return opts; } bool OptionList::has_option(std::string name) const noexcept{ //name = std::regex_replace(name, std::regex("[ _]"), "-"); return std::any_of(options_.begin(), options_.end(), [name](const OptionBase &x) { return x.name() == name; }); } void OptionList::from_yaml(const YAML::Node &node, const option_error_handler &handler, bool check){ if (!node.IsMap()) { throw std::runtime_error("Expecting YAML map."); } if(check){ for (YAML::const_iterator it = node.begin(); it != node.end(); ++it) { std::string basename = it->first.as(); bool exist = has_option(basename); if(it->second.IsMap() and !exist){ for (YAML::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { if(!has_option(basename + "/" + it2->first.as())) throw std::runtime_error("This is not a valid option: "+ basename + "/" + it2->first.as()+ ".\n Possible values are: "+ list_options()); } }else if (!exist) throw std::runtime_error("This is not a valid option: "+ basename+ ".\n Possible values are: "+ list_options()); } } YAML::Node x; // loop through options for (auto &option : options_) { // check if available in YAML node // treat "/" in option name special (e.g. recurse into maps) if (get_nested_yaml_node(node, option.path(), x)) { if (x.IsNull()) { if (option.is_nullable()) { option.set_null(); continue; } else { throw std::runtime_error("Error setting option " + option.name() + ": value cannot be null"); } } try { option.from_yaml(x); } catch (ConversionError &e) { if (!handler || !handler(option.name(), option.is_required(), OptionError::conversion_from_yaml_failed, e.what())) { throw std::runtime_error("Error setting option " + option.name() + ": " + e.what()); } } catch (ValidationError &e) { if (!handler || !handler(option.name(), option.is_required(), OptionError::validation_failed, e.what())) { throw std::runtime_error("Error setting option " + option.name() + ": " + e.what()); } } } else if (option.is_required()) { if (!handler || !handler(option.name(), option.is_required(), OptionError::requirement_failed, "")) { throw std::runtime_error("Missing required option " + option.name() + "."); } } } } YAML::Node OptionList::to_yaml(const option_error_handler &handler) const { YAML::Node root = YAML::Node(YAML::NodeType::Map); for (auto &option : options_) { YAML::Node n; if (!option.is_nullable() || !option.is_null()) { try { n = option.to_yaml(); } catch (ConversionError &e) { if (handler && !handler(option.name(), option.is_required(), OptionError::conversion_to_yaml_failed, e.what())) { throw std::runtime_error("Error exporting option " + option.name() + ": " + e.what()); } } catch (SkipError &e) { continue; } catch (...) { throw std::runtime_error("Unknown error for option " + option.name()); } } set_nested_yaml_node(root, option.path(), n); } return root; } void OptionList::load_yaml(std::string filename, const option_error_handler &handler) { YAML::Node root = YAML::LoadFile(filename); from_yaml(root, handler); } void OptionList::save_yaml(std::string filename, const option_error_handler &handler) const { auto node = to_yaml(handler); YAML::Emitter yaml_emitter; yaml_emitter << node; std::ofstream out(filename); out << yaml_emitter.c_str(); }