Files
scylladb/tools/utils.hh
Botond Dénes 2d26613f28 tools: move operation-options to the operations themselves
Currently, operation-options are declared in a single global list, then
operations refer to the options they support via name. This system was
born at a time, when scylla-sstable had a lot of shared options between
its operations, so it was desirable to declare them centrally and only
add references to individual operations, to reduce duplication.
However, as the dust settled, only 2 options are shared by 2 operations
each. This is a very low benefit. Up to now the cost was also very low
-- shared options meant the same in all operations that used them.
However this is about to change and this system becomes very awkward to
use as soon as multiple operations want to have an option with the same
name, but sligthly (or very) different meaning/semantics.
So this patch changes moves the options to the operations themselves.
Each will declare the list of options it supports, without having to
reference some common list.
This also removes an entire (although very uncommon) class of bugs:
option-name referring to inexistent option.

Closes #14898
2023-07-31 20:16:41 +03:00

122 lines
4.0 KiB
C++

/*
* Copyright (C) 2021-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <boost/program_options.hpp>
#include <seastar/core/app-template.hh>
#include "seastarx.hh"
namespace tools::utils {
class basic_option {
public:
const char* name;
const char* description;
public:
basic_option(const char* name, const char* description) : name(name), description(description) { }
virtual void add_option(boost::program_options::options_description& opts) const = 0;
};
template <typename T = std::monostate>
class typed_option : public basic_option {
std::optional<T> _default_value;
int _count;
virtual void add_option(boost::program_options::options_description& opts) const override {
if (_default_value) {
opts.add_options()(name, boost::program_options::value<T>()->default_value(*_default_value), description);
} else {
opts.add_options()(name, boost::program_options::value<T>(), description);
}
}
public:
typed_option(const char* name, const char* description) : basic_option(name, description) { }
typed_option(const char* name, T default_value, const char* description) : basic_option(name, description), _default_value(std::move(default_value)) { }
typed_option(const char* name, const char* description, int count) : basic_option(name, description), _count(count) { }
};
template <>
class typed_option<std::monostate> : public basic_option {
virtual void add_option(boost::program_options::options_description& opts) const override {
opts.add_options()(name, description);
}
public:
typed_option(const char* name, const char* description) : basic_option(name, description) { }
};
class operation_option {
shared_ptr<basic_option> _opt; // need copy to support convenient range declaration of std::vector<option>
public:
template <typename T>
operation_option(typed_option<T> opt) : _opt(make_shared<typed_option<T>>(std::move(opt))) { }
const char* name() const { return _opt->name; }
const char* description() const { return _opt->description; }
void add_option(boost::program_options::options_description& opts) const { _opt->add_option(opts); }
};
class operation {
std::string _name;
std::string _summary;
std::string _description;
std::vector<operation_option> _options;
std::vector<app_template::positional_option> _positional_options;
public:
operation(
std::string name,
std::string summary,
std::string description,
std::vector<operation_option> options = {},
std::vector<app_template::positional_option> positional_options = {})
: _name(std::move(name))
, _summary(std::move(summary))
, _description(std::move(description))
, _options(std::move(options))
, _positional_options(std::move(positional_options)) {
}
const std::string& name() const { return _name; }
const std::string& summary() const { return _summary; }
const std::string& description() const { return _description; }
const std::vector<operation_option>& options() const { return _options; }
const std::vector<app_template::positional_option>& positional_options() const { return _positional_options; }
};
inline bool operator<(const operation& a, const operation& b) {
return a.name() < b.name();
}
class tool_app_template {
public:
struct config {
sstring name;
sstring description;
sstring logger_name;
size_t lsa_segment_pool_backend_size_mb = 1;
std::vector<operation> operations;
const std::vector<operation_option>* global_options = nullptr;
const std::vector<app_template::positional_option>* global_positional_options = nullptr;
};
private:
config _cfg;
public:
tool_app_template(config cfg)
: _cfg(std::move(cfg))
{ }
int run_async(int argc, char** argv, noncopyable_function<int(const operation&, const boost::program_options::variables_map&)> main_func);
};
} // namespace tools::utils