/* * Copyright (C) 2015-present ScyllaDB * */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 */ #pragma once #include #include #include #include #include #include #include "config_file.hh" namespace utils { template T config_from_string(std::string_view string_representation) { return boost::lexical_cast(string_representation); } template <> bool config_from_string(std::string_view string_representation); template <> sstring config_from_string(std::string_view string_representation); } namespace YAML { /* * Add converters as needed here... * * TODO: Maybe we should just define all node conversions as "lexical_cast". * However, vanilla yamp-cpp does some special treatment of scalar types, * mainly inf handling etc. Hm. */ template<> struct convert { static bool decode(const Node& node, sstring& rhs) { std::string tmp; if (!convert::decode(node, tmp)) { return false; } rhs = tmp; return true; } }; template struct convert> { using map_type = std::unordered_map; static bool decode(const Node& node, map_type& rhs) { if (!node.IsMap()) { return false; } rhs.clear(); for (auto& n : node) { rhs[n.first.as()] = n.second.as(); } return true; } }; } namespace std { template std::istream& operator>>(std::istream&, std::unordered_map&); template<> std::istream& operator>>(std::istream&, std::unordered_map&); template std::istream& operator>>(std::istream&, std::vector&); template<> std::istream& operator>>(std::istream&, std::vector&); extern template std::istream& operator>>(std::istream&, std::vector&); template std::istream& operator>>(std::istream& is, std::unordered_map& map) { std::unordered_map tmp; is >> tmp; for (auto& p : tmp) { map[utils::config_from_string(p.first)] = utils::config_from_string(p.second); } return is; } template std::istream& operator>>(std::istream& is, std::vector& dst) { std::vector tmp; is >> tmp; for (auto& v : tmp) { dst.emplace_back(utils::config_from_string(v)); } return is; } template void validate(boost::any& out, const std::vector& in, std::unordered_map*, int utf8) { using map_type = std::unordered_map; if (out.empty()) { out = boost::any(map_type()); } static const boost::regex key(R"foo((?:^|\:)([^=:]+)=)foo"); auto* p = boost::any_cast(&out); for (const auto& s : in) { boost::sregex_iterator i(s.begin(), s.end(), key), e; if (i == e) { throw boost::program_options::invalid_option_value(s); } while (i != e) { auto k = (*i)[1].str(); auto vs = s.begin() + i->position() + i->length(); auto ve = s.end(); if (++i != e) { ve = s.begin() + i->position(); } (*p)[utils::config_from_string(k)] = utils::config_from_string(sstring(vs, ve)); } } } } namespace utils { /* * Our own bpo::typed_valye. * Only difference is that we _don't_ apply defaults (they are already applied) * Needed to make aliases work properly. */ template class typed_value_ex : public bpo::typed_value { public: typedef bpo::typed_value _Super; typed_value_ex() : _Super(nullptr) {} bool apply_default(boost::any& value_store) const override { return false; } }; template void maybe_multitoken(typed_value_ex* r) { } template void maybe_multitoken(std::vector>* r) { r->multitoken(); } template inline typed_value_ex* value_ex() { typed_value_ex* r = new typed_value_ex(); maybe_multitoken(r); return r; } sstring hyphenate(const std::string_view&); } template void utils::config_file::named_value::add_command_line_option(boost::program_options::options_description_easy_init& init) { const auto hyphenated_name = hyphenate(name()); // NOTE. We are not adding default values. We could, but must in that case manually (in some way) generate the textual // version, since the available ostream operators for things like pairs and collections don't match what we can deal with parser-wise. // See removed ostream operators above. init(hyphenated_name.data(), value_ex()->notifier([this](T new_val) { try { set(std::move(new_val), config_source::CommandLine); } catch (const std::invalid_argument& e) { throw bpo::invalid_option_value(e.what()); } }), desc().data()); if (!alias().empty()) { const auto alias_desc = fmt::format("Alias for {}", hyphenated_name); init(hyphenate(alias()).data(), value_ex()->notifier([this](T new_val) { set(std::move(new_val), config_source::CommandLine); }), alias_desc.data()); } } template void utils::config_file::named_value::set_value(const YAML::Node& node, config_source previous_src) { if (previous_src == config_source::SettingsFile && _liveness != liveness::LiveUpdate) { return; } (*this)(node.as()); _source = config_source::SettingsFile; } template bool utils::config_file::named_value::set_value(sstring value, config_source src) { if ((_liveness != liveness::LiveUpdate) || (src == config_source::CQL && !_cf->are_live_updatable_config_params_changeable_via_cql())) { return false; } (*this)(config_from_string(value), src); return true; } template future utils::config_file::named_value::set_value_on_all_shards(sstring value, config_source src) { if ((_liveness != liveness::LiveUpdate) || (src == config_source::CQL && !_cf->are_live_updatable_config_params_changeable_via_cql())) { co_return false; } co_await smp::invoke_on_all([this, value = config_from_string(value), src] () { (*this)(value, src); }); co_return true; }