Unverified Commit 3248e4b2 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #827

 Add marker to make categories optional on the CLI
parents b01cc5e8 7c2a4f2e
......@@ -100,6 +100,9 @@ public:
/// Returns whether this config option stores a boolean flag.
bool is_flag() const noexcept;
/// Returns whether the category is optional for CLI options.
bool has_flat_cli_name() const noexcept;
/// Tries to parse an input string. Stores and returns the parsed value on
/// success, returns an error otherwise.
expected<config_value> parse(string_view input) const;
......
......@@ -55,7 +55,7 @@ config_option::config_option(string_view category, string_view name,
CAF_ASSERT(ts <= std::numeric_limits<uint16_t>::max());
buf_size_ = static_cast<uint16_t>(ts);
buf_.reset(new char[ts]);
// fille the buffer with "<category>.<long-name>,<short-name>,<descriptions>"
// fill the buffer with "<category>.<long-name>,<short-name>,<descriptions>"
auto first = buf_.get();
auto i = first;
auto pos = [&] {
......@@ -109,7 +109,7 @@ void swap(config_option& first, config_option& second) noexcept {
// -- properties ---------------------------------------------------------------
string_view config_option::category() const noexcept {
return buf_slice(0, category_separator_);
return buf_slice(buf_[0] == '?' ? 1 : 0, category_separator_);
}
string_view config_option::long_name() const noexcept {
......@@ -125,7 +125,7 @@ string_view config_option::description() const noexcept {
}
string_view config_option::full_name() const noexcept {
return buf_slice(0, long_name_separator_);
return buf_slice(buf_[0] == '?' ? 1 : 0, long_name_separator_);
}
error config_option::check(const config_value& x) const {
......@@ -148,6 +148,10 @@ bool config_option::is_flag() const noexcept {
return type_name() == "boolean";
}
bool config_option::has_flat_cli_name() const noexcept {
return buf_[0] == '?' || category() == "global";
}
expected<config_value> config_option::parse(string_view input) const {
return meta_->parse(value_, input);
}
......
......@@ -64,7 +64,7 @@ std::string config_option_set::help_text(bool global_only) const {
string_builder sb;
if (x.short_names().empty()) {
sb << " --";
if (x.category() != "global")
if (!x.has_flat_cli_name())
sb << x.category() << '.';
sb << x.long_name();
if (!x.is_flag())
......@@ -74,7 +74,7 @@ std::string config_option_set::help_text(bool global_only) const {
for (auto c : x.short_names())
sb << '-' << c << '|';
sb << "--";
if (x.category() != "global")
if (!x.has_flat_cli_name())
sb << x.category() << '.';
sb << x.long_name() << ") ";
}
......@@ -88,11 +88,15 @@ std::string config_option_set::help_text(bool global_only) const {
std::multimap<string_view, pair> args;
size_t max_arg_size = 0;
for (auto& opt : opts_) {
if (!global_only || opt.category() == "global") {
// We treat all options with flat name as-if the category was 'global'.
if (!global_only || opt.has_flat_cli_name()) {
auto arg = build_argument(opt);
max_arg_size = std::max(max_arg_size, arg.size());
categories.emplace(opt.category());
args.emplace(opt.category(), std::make_pair(std::move(arg), &opt));
string_view category = "global";
if (!opt.has_flat_cli_name())
category = opt.category();
categories.emplace(category);
args.emplace(category, std::make_pair(std::move(arg), &opt));
}
}
// Build help text by iterating over all categories in the multimap.
......@@ -227,18 +231,18 @@ config_option_set::cli_long_name_lookup(string_view name) const {
string_view long_name;
auto sep = name.find('.', offset);
if (sep == string::npos) {
category = "global";
if (offset == 0)
long_name = name;
else
long_name = name.substr(offset);
long_name = name.substr(offset);
} else {
category = name.substr(offset, sep);
long_name = name.substr(sep + 1);
}
// Scan all options for a match.
auto category_match = [&](const config_option& opt) {
return sep == string::npos ? opt.has_flat_cli_name()
: opt.category() == category;
};
return detail::ptr_find_if(opts_, [&](const config_option& opt) {
return opt.category() == category && opt.long_name() == long_name;
return category_match(opt) && opt.long_name() == long_name;
});
}
......
......@@ -138,7 +138,8 @@ DEFAULT_META(size_t, default_config_option_parse<size_t>)
DEFAULT_META(string, parse_string)
config_option::meta_state bool_meta_state{bool_check, bool_store, bool_get,
nullptr, detail::type_name<bool>()};
default_config_option_parse<bool>,
detail::type_name<bool>()};
} // namespace detail
......
......@@ -47,7 +47,7 @@ constexpr int64_t underflow() {
template <class T>
optional<T> read(string_view arg) {
auto co = make_config_option<T>(category, name, explanation);
auto res = config_value::parse(arg);
auto res = co.parse(arg);
if (res && holds_alternative<T>(*res)) {
CAF_CHECK_EQUAL(co.check(*res), none);
return get<T>(*res);
......@@ -167,16 +167,26 @@ CAF_TEST(type double) {
}
CAF_TEST(type string) {
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("foo")), "foo");
CAF_CHECK_EQUAL(unbox(read<string>("\"foo\"")), "\"foo\"");
}
CAF_TEST(type atom) {
CAF_CHECK_EQUAL(unbox(read<atom_value>("'foo'")), atom("foo"));
CAF_CHECK_EQUAL(read<atom_value>("bar"), none);
CAF_CHECK_EQUAL(unbox(read<atom_value>("foo")), atom("foo"));
CAF_CHECK_EQUAL(read<atom_value>("toomanycharacters"), none);
CAF_CHECK_EQUAL(read<atom_value>("illegal!"), none);
}
CAF_TEST(type timespan) {
timespan dur{500};
CAF_CHECK_EQUAL(unbox(read<timespan>("500ns")), dur);
}
CAF_TEST(flat CLI parsing) {
auto x = make_config_option<std::string>("?foo", "bar,b", "test option");
CAF_CHECK_EQUAL(x.category(), "foo");
CAF_CHECK_EQUAL(x.long_name(), "bar");
CAF_CHECK_EQUAL(x.short_names(), "b");
CAF_CHECK_EQUAL(x.full_name(), "foo.bar");
CAF_CHECK_EQUAL(x.has_flat_cli_name(), true);
}
......@@ -39,16 +39,18 @@ struct fixture {
config_option_set opts;
template <class T>
expected<T> read(std::vector<std::string> args) {
expected<T> read(std::vector<std::string> args) {
settings cfg;
auto res = opts.parse(cfg, std::move(args));
if (res.first != pec::success)
return res.first;
auto x = get_if<T>(&cfg, "value");
auto x = get_if<T>(&cfg, key);
if (x == none)
return sec::invalid_argument;
return *x;
}
std::string key = "value";
};
} // namespace <anonymous>
......@@ -140,4 +142,13 @@ CAF_TEST(string parameters) {
CAF_CHECK_EQUAL(read<std::string>({"-v123"}), "123");
}
CAF_TEST(flat CLI options) {
key = "foo.bar";
opts.add<std::string>("?foo", "bar,b", "some value");
CAF_CHECK(opts.begin()->has_flat_cli_name());
CAF_CHECK_EQUAL(read<std::string>({"-b", "foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"--bar=foobar"}), "foobar");
CAF_CHECK_EQUAL(read<std::string>({"--foo.bar=foobar"}), "foobar");
}
CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment