Files
scylladb/utils/config_file_impl.hh
Kefu Chai 9fdbe0e74b tree: Remove unused boost headers
This commit eliminates unused boost header includes from the tree.

Removing these unnecessary includes reduces dependencies on the
external Boost.Adapters library, leading to faster compile times
and a slightly cleaner codebase.

Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>

Closes scylladb/scylladb#22997
2025-02-25 10:32:32 +03:00

239 lines
6.7 KiB
C++

/*
* Copyright (C) 2015-present ScyllaDB
*
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
*/
#pragma once
#include <boost/regex.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/value_semantic.hpp>
#include <boost/lexical_cast.hpp>
#include <yaml-cpp/node/convert.h>
#include <seastar/core/smp.hh>
#include "config_file.hh"
namespace utils {
template <typename T>
T config_from_string(std::string_view string_representation) {
return boost::lexical_cast<T>(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<seastar::sstring> {
static bool decode(const Node& node, sstring& rhs) {
std::string tmp;
if (!convert<std::string>::decode(node, tmp)) {
return false;
}
rhs = tmp;
return true;
}
};
template<typename K, typename V, typename... Rest>
struct convert<std::unordered_map<K, V, Rest...>> {
using map_type = std::unordered_map<K, V, Rest...>;
static bool decode(const Node& node, map_type& rhs) {
if (!node.IsMap()) {
return false;
}
rhs.clear();
for (auto& n : node) {
rhs[n.first.as<K>()] = n.second.as<V>();
}
return true;
}
};
}
namespace std {
template<typename K, typename V, typename... Args>
std::istream& operator>>(std::istream&, std::unordered_map<K, V, Args...>&);
template<>
std::istream& operator>>(std::istream&, std::unordered_map<seastar::sstring, seastar::sstring>&);
template<typename V, typename... Args>
std::istream& operator>>(std::istream&, std::vector<V, Args...>&);
template<>
std::istream& operator>>(std::istream&, std::vector<seastar::sstring>&);
extern template
std::istream& operator>>(std::istream&, std::vector<seastar::sstring>&);
template<typename K, typename V, typename... Args>
std::istream& operator>>(std::istream& is, std::unordered_map<K, V, Args...>& map) {
std::unordered_map<sstring, sstring> tmp;
is >> tmp;
for (auto& p : tmp) {
map[utils::config_from_string<K>(p.first)] = utils::config_from_string<V>(p.second);
}
return is;
}
template<typename V, typename... Args>
std::istream& operator>>(std::istream& is, std::vector<V, Args...>& dst) {
std::vector<seastar::sstring> tmp;
is >> tmp;
for (auto& v : tmp) {
dst.emplace_back(utils::config_from_string<V>(v));
}
return is;
}
template<typename K, typename V, typename... Args>
void validate(boost::any& out, const std::vector<std::string>& in, std::unordered_map<K, V, Args...>*, int utf8) {
using map_type = std::unordered_map<K, V, Args...>;
if (out.empty()) {
out = boost::any(map_type());
}
static const boost::regex key(R"foo((?:^|\:)([^=:]+)=)foo");
auto* p = boost::any_cast<map_type>(&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>(k)] = utils::config_from_string<V>(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 T, class charT = char>
class typed_value_ex : public bpo::typed_value<T, charT> {
public:
typedef bpo::typed_value<T, charT> _Super;
typed_value_ex()
: _Super(nullptr)
{}
bool apply_default(boost::any& value_store) const override {
return false;
}
};
template <typename T>
void maybe_multitoken(typed_value_ex<T>* r) {
}
template <typename T>
void maybe_multitoken(std::vector<typed_value_ex<T>>* r) {
r->multitoken();
}
template<class T>
inline typed_value_ex<T>* value_ex() {
typed_value_ex<T>* r = new typed_value_ex<T>();
maybe_multitoken(r);
return r;
}
sstring hyphenate(const std::string_view&);
}
template<typename T>
void utils::config_file::named_value<T>::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<T>()->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<T>()->notifier([this](T new_val) { set(std::move(new_val), config_source::CommandLine); }), alias_desc.data());
}
}
template<typename T>
void utils::config_file::named_value<T>::set_value(const YAML::Node& node, config_source previous_src) {
if (previous_src == config_source::SettingsFile && _liveness != liveness::LiveUpdate) {
return;
}
(*this)(node.as<T>());
_source = config_source::SettingsFile;
}
template<typename T>
bool utils::config_file::named_value<T>::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<T>(value), src);
return true;
}
template<typename T>
future<bool> utils::config_file::named_value<T>::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<T>(value), src] () {
(*this)(value, src);
});
co_return true;
}