Commit 7ac22638 authored by Samir Halilcevic's avatar Samir Halilcevic

Enable space separated value syntax for long argument names

parent 87d449a8
......@@ -104,37 +104,14 @@ private:
mutable void* value_;
};
/// Finds `config_option` string with a matching long name in (`first`, `last`],
/// where each entry is a pointer to a string. Returns a `ForwardIterator` to
/// the match and a `string_view` of the option value if the entry is
/// found and a `ForwardIterator` to `last` with an empty `string_view`
/// Finds `config_option` string with a matching long name in the string
/// argument list [`first`, `last`). Returns an `argument_iterator` to the
/// matching location and a `string_view` of the option value if the entry is
/// found, or a `arugment_iterator` to `last` with an empty `string_view`
/// otherwise.
template <class ForwardIterator, class Sentinel>
std::pair<ForwardIterator, std::string_view>
find_by_long_name(const config_option& x, ForwardIterator first,
Sentinel last) {
auto long_name = x.long_name();
for (; first != last; ++first) {
std::string_view str{*first};
// Make sure this is a long option starting with "--".
if (!starts_with(str, "--"))
continue;
str.remove_prefix(2);
// Skip optional "caf#" prefix.
if (starts_with(str, "caf#"))
str.remove_prefix(4);
// Make sure we are dealing with the right key.
if (!starts_with(str, long_name))
continue;
// Make sure the key is followed by an assignment.
str.remove_prefix(long_name.size());
if (!starts_with(str, "="))
continue;
// Remove leading '=' and return the value.
str.remove_prefix(1);
return {first, str};
}
return {first, std::string_view{}};
}
std::pair<std::vector<std::string>::const_iterator, std::string_view>
find_by_long_name(const config_option& x,
std::vector<std::string>::const_iterator first,
std::vector<std::string>::const_iterator last);
} // namespace caf
......@@ -462,9 +462,7 @@ std::pair<error, std::string>
actor_system_config::extract_config_file_path(string_list& args) {
auto ptr = custom_options_.qualified_name_lookup("global.config-file");
CAF_ASSERT(ptr != nullptr);
string_list::iterator i;
std::string_view path;
std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
auto [i, path] = find_by_long_name(*ptr, args.begin(), args.end());
if (i == args.end()) {
return {none, std::string{}};
} else if (path.empty()) {
......
......@@ -133,4 +133,38 @@ std::string_view config_option::buf_slice(size_t from,
return {buf_.get() + from, to - from};
}
// TODO: consider deprecating this
std::pair<std::vector<std::string>::const_iterator, std::string_view>
find_by_long_name(const config_option& x,
std::vector<std::string>::const_iterator first,
std::vector<std::string>::const_iterator last) {
auto long_name = x.long_name();
for (; first != last; ++first) {
std::string_view str{*first};
// Make sure this is a long option starting with "--".
if (!starts_with(str, "--"))
continue;
str.remove_prefix(2);
// Make sure we are dealing with the right key.
if (!starts_with(str, long_name))
continue;
str.remove_prefix(long_name.size());
// check for flag
if (x.is_flag() && str.empty()) {
return {first, str};
} else if (starts_with(str, "=")) {
// Remove leading '=' and return the value.
str.remove_prefix(1);
return {first, str};
} else if (str.empty() && (first + 1) != last) {
// Get the next argument as value
++first;
return {first, std::string_view{*first}};
} else {
continue;
}
}
return {first, std::string_view{}};
}
} // namespace caf
......@@ -175,15 +175,18 @@ auto config_option_set::parse(settings& config, argument_iterator first,
if (*i == "--")
return {pec::success, std::next(first)};
if (i->compare(0, 2, "--") == 0) {
// Long options use the syntax "--<name>=<value>" and consume only a
// single argument.
auto npos = std::string::npos;
// Long options cone in three variaties:
// syntax "--<name>=<value>" formated as a single single argument,
// sytnax "--<name> <value>", comes as two arguments
// special case "--<name>", boolean flag
const auto npos = std::string::npos;
auto assign_op = i->find('=');
auto name = assign_op == npos ? i->substr(2)
: i->substr(2, assign_op - 2);
auto opt = cli_long_name_lookup(name);
if (opt == nullptr)
return {pec::not_an_option, i};
if (opt->is_flag() || assign_op != npos) {
auto code
= consume(*opt,
assign_op == npos
......@@ -193,6 +196,16 @@ auto config_option_set::parse(settings& config, argument_iterator first,
if (code != pec::success)
return {code, i};
++i;
} else {
auto j = std::next(i);
if (j == last) {
return {pec::missing_argument, j};
}
auto code = consume(*opt, j->begin(), j->end());
if (code != pec::success)
return {code, i};
std::advance(i, 2);
}
} else if (i->front() == '-') {
// Short options have three possibilities.
auto opt = cli_short_name_lookup((*i)[1]);
......
......@@ -352,20 +352,20 @@ CAF_TEST(find by long opt) {
};
// Well formed, find val2.
check({"--foo=val1", "--bar=val2", "--baz=val3"}, true, true);
check({"--foo=val1", "--bar", "val2", "--baz=val3"}, true, true);
// Dashes missing, no match.
check({"--foo=val1", "bar=val2", "--baz=val3"}, false, false);
// Equal missing.
check({"--fooval1", "--barval2", "--bazval3"}, false, false);
// Option value missing.
check({"--foo=val1", "--bar=", "--baz=val3"}, true, false);
// With prefix 'caf#'.
check({"--caf#foo=val1", "--caf#bar=val2", "--caf#baz=val3"}, true, true);
// Option not included.
check({"--foo=val1", "--b4r=val2", "--baz=val3"}, false, false);
// Option not included, with prefix.
check({"--caf#foo=val1", "--caf#b4r=val2", "--caf#baz=val3"}, false, false);
// No options to look through.
check({}, false, false);
// flag option for booleans
needle = make_config_option<bool>("?foo"sv, "bar,b"sv, "test option"sv);
check({"--foo=val1", "--bar", "--baz=val3"}, true, false);
}
END_FIXTURE_SCOPE()
......@@ -109,9 +109,22 @@ CAF_TEST(parse with ref syncing) {
CHECK_EQ(get_as<int>(cfg, "foo.i"), 42);
}
CAF_TEST(Long format for flags) {
auto foo_b = false;
opts.add<bool>(foo_b, "foo", "b,b", "");
settings cfg;
vector<string> args{"--foo.b"};
auto res = opts.parse(cfg, args);
CHECK_EQ(res.first, pec::success);
if (res.second != args.end())
CAF_FAIL("parser stopped at: " << *res.second);
CHECK_EQ(foo_b, true);
}
CAF_TEST(string parameters) {
opts.add<std::string>("value,v", "some value");
CHECK_EQ(read<std::string>({"--value=foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--value", "foobar"}), "foobar");
CHECK_EQ(read<std::string>({"-v", "foobar"}), "foobar");
CHECK_EQ(read<std::string>({"-vfoobar"}), "foobar");
}
......@@ -121,8 +134,11 @@ CAF_TEST(flat CLI options) {
opts.add<std::string>("?foo", "bar,b", "some value");
CHECK(opts.begin()->has_flat_cli_name());
CHECK_EQ(read<std::string>({"-b", "foobar"}), "foobar");
CHECK_EQ(read<std::string>({"-bfoobar"}), "foobar");
CHECK_EQ(read<std::string>({"--bar=foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--foo.bar=foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--bar", "foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--foo.bar", "foobar"}), "foobar");
}
CAF_TEST(flat CLI parsing with nested categories) {
......@@ -130,8 +146,11 @@ CAF_TEST(flat CLI parsing with nested categories) {
opts.add<std::string>("?foo.goo", "bar,b", "some value");
CHECK(opts.begin()->has_flat_cli_name());
CHECK_EQ(read<std::string>({"-b", "foobar"}), "foobar");
CHECK_EQ(read<std::string>({"-bfoobar"}), "foobar");
CHECK_EQ(read<std::string>({"--bar=foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--foo.goo.bar=foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--bar", "foobar"}), "foobar");
CHECK_EQ(read<std::string>({"--foo.goo.bar", "foobar"}), "foobar");
}
CAF_TEST(square brackets are optional on the command line) {
......@@ -146,6 +165,15 @@ CAF_TEST(square brackets are optional on the command line) {
CHECK_EQ(read<int_list>({"--value=1"}), int_list({1}));
CHECK_EQ(read<int_list>({"--value=1,2,3"}), int_list({1, 2, 3}));
CHECK_EQ(read<int_list>({"--value=1, 2 , 3 , "}), int_list({1, 2, 3}));
CHECK_EQ(read<int_list>({"--value", "[1]"}), int_list({1}));
CHECK_EQ(read<int_list>({"--value", "[1,]"}), int_list({1}));
CHECK_EQ(read<int_list>({"--value", "[ 1 , ]"}), int_list({1}));
CHECK_EQ(read<int_list>({"--value", "[1,2]"}), int_list({1, 2}));
CHECK_EQ(read<int_list>({"--value", "[1, 2, 3]"}), int_list({1, 2, 3}));
CHECK_EQ(read<int_list>({"--value", "[1, 2, 3, ]"}), int_list({1, 2, 3}));
CHECK_EQ(read<int_list>({"--value", "1"}), int_list({1}));
CHECK_EQ(read<int_list>({"--value", "1,2,3"}), int_list({1, 2, 3}));
CHECK_EQ(read<int_list>({"--value", "1, 2 , 3 , "}), int_list({1, 2, 3}));
CHECK_EQ(read<int_list>({"-v", "[1]"}), int_list({1}));
CHECK_EQ(read<int_list>({"-v", "[1,]"}), int_list({1}));
CHECK_EQ(read<int_list>({"-v", "[ 1 , ]"}), int_list({1}));
......
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