Commit 7f414346 authored by Dominik Charousset's avatar Dominik Charousset

Fix handling of CLI arguments

parent 844ad3db
...@@ -785,47 +785,6 @@ private: ...@@ -785,47 +785,6 @@ private:
} }
}; };
template <class T>
struct inspect_config_value_access {
static std::string type_name() {
return to_string(type_name_or_anonymous<T>());
}
static optional<T> get_if(const config_value* x) {
config_value_reader reader{x};
auto tmp = T{};
if (detail::load_value(reader, tmp))
return optional<T>{std::move(tmp)};
return none;
}
static bool is(const config_value& x) {
return get_if(&x) != none;
}
static T get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
template <class Nested>
static void parse_cli(string_parser_state& ps, T&, Nested) {
// TODO: we could try to read a config_value here and then deserialize the
// value from that.
ps.code = pec::invalid_argument;
}
static config_value convert(const T& x) {
config_value result;
config_value_writer writer{&result};
if (!detail::save_value(writer, x))
CAF_RAISE_ERROR("unable to convert type to a config_value");
return result;
}
};
} // namespace detail } // namespace detail
// -- SumType access of dictionary values -------------------------------------- // -- SumType access of dictionary values --------------------------------------
...@@ -932,6 +891,68 @@ config_value make_config_value_list(Ts&&... xs) { ...@@ -932,6 +891,68 @@ config_value make_config_value_list(Ts&&... xs) {
// -- inspection API ----------------------------------------------------------- // -- inspection API -----------------------------------------------------------
namespace detail {
template <class T>
struct inspect_config_value_access {
static std::string type_name() {
return to_string(type_name_or_anonymous<T>());
}
static optional<T> get_if(const config_value* x) {
config_value_reader reader{x};
auto tmp = T{};
if (detail::load_value(reader, tmp))
return optional<T>{std::move(tmp)};
return none;
}
static bool is(const config_value& x) {
return get_if(&x) != none;
}
static T get(const config_value& x) {
auto result = get_if(&x);
if (!result)
CAF_RAISE_ERROR("invalid type found");
return std::move(*result);
}
template <class Nested>
static void parse_cli(string_parser_state& ps, T& x, Nested token) {
auto first = ps.i;
config_value tmp;
detail::parse(ps, tmp);
// If the first attempt fails, rewind the parser state and try parsing the
// input as a string. This allows unescaped inputs.
if (ps.code != pec::success) {
ps.code = pec::success;
ps.i = first;
std::string str;
config_value_access_t<std::string>::parse_cli(ps, str, token);
if (ps.code == pec::success)
tmp = config_value{std::move(str)};
}
if (ps.code == pec::success) {
if (auto res = caf::get_if<T>(&tmp)) {
x = detail::move_if_not_ptr(res);
} else {
ps.code = pec::invalid_argument;
}
}
}
static config_value convert(const T& x) {
config_value result;
config_value_writer writer{&result};
if (!detail::save_value(writer, x))
CAF_RAISE_ERROR("unable to convert type to a config_value");
return result;
}
};
} // namespace detail
template <> template <>
struct variant_inspector_traits<config_value> { struct variant_inspector_traits<config_value> {
using value_type = config_value; using value_type = config_value;
......
...@@ -115,6 +115,8 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_endpoint& x); ...@@ -115,6 +115,8 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, ipv6_endpoint& x);
CAF_CORE_EXPORT void parse(string_parser_state& ps, uri& x); CAF_CORE_EXPORT void parse(string_parser_state& ps, uri& x);
CAF_CORE_EXPORT void parse(string_parser_state& ps, config_value& x);
// -- variadic utility --------------------------------------------------------- // -- variadic utility ---------------------------------------------------------
template <class... Ts> template <class... Ts>
......
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
#include "caf/detail/parse.hpp" #include "caf/detail/parse.hpp"
#include "caf/detail/config_consumer.hpp"
#include "caf/detail/consumer.hpp" #include "caf/detail/consumer.hpp"
#include "caf/detail/parser/chars.hpp" #include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/read_bool.hpp" #include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_config.hpp"
#include "caf/detail/parser/read_floating_point.hpp" #include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/read_ipv4_address.hpp" #include "caf/detail/parser/read_ipv4_address.hpp"
#include "caf/detail/parser/read_ipv6_address.hpp" #include "caf/detail/parser/read_ipv6_address.hpp"
...@@ -161,6 +163,21 @@ void parse(string_parser_state& ps, uri& x) { ...@@ -161,6 +163,21 @@ void parse(string_parser_state& ps, uri& x) {
x = builder.make(); x = builder.make();
} }
void parse(string_parser_state& ps, config_value& x) {
ps.skip_whitespaces();
if (ps.at_end()) {
ps.code = pec::unexpected_eof;
return;
}
// Safe the string as fallback.
string_view str{ps.i, ps.e};
// Dispatch to parser.
detail::config_value_consumer f;
parser::read_config_value(ps, f);
if (ps.code <= pec::trailing_character)
x = std::move(f.result);
}
PARSE_IMPL(ipv4_address, ipv4_address) PARSE_IMPL(ipv4_address, ipv4_address)
void parse(string_parser_state& ps, ipv4_subnet& x) { void parse(string_parser_state& ps, ipv4_subnet& x) {
......
...@@ -18,15 +18,15 @@ ...@@ -18,15 +18,15 @@
#define CAF_SUITE config_option_set #define CAF_SUITE config_option_set
#include "caf/config_option_set.hpp"
#include "core-test.hpp"
#include "inspector-tests.hpp"
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "caf/config.hpp"
#include "core-test.hpp"
#include "caf/config_option_set.hpp"
#include "caf/detail/move_if_not_ptr.hpp" #include "caf/detail/move_if_not_ptr.hpp"
#include "caf/settings.hpp" #include "caf/settings.hpp"
...@@ -236,4 +236,14 @@ CAF_TEST(CLI arguments override defaults) { ...@@ -236,4 +236,14 @@ CAF_TEST(CLI arguments override defaults) {
} }
} }
CAF_TEST(CLI arguments may use custom types) {
settings cfg;
opts.add<foobar>("global", "foobar,f", "test option");
CAF_CHECK_EQUAL(read<foobar>(cfg, {"-f{foo=\"hello\",bar=\"world\"}"}), none);
CAF_MESSAGE("CFG: "<<cfg);
auto fb = get_if<foobar>(&cfg, "foobar");
if (CAF_CHECK(fb))
CAF_CHECK_EQUAL(*fb, foobar("hello", "world"));
}
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -489,8 +489,17 @@ CAF_TEST(config values pick up user defined inspect overloads) { ...@@ -489,8 +489,17 @@ CAF_TEST(config values pick up user defined inspect overloads) {
CAF_MESSAGE("read 'line' via get_if and verify the object"); CAF_MESSAGE("read 'line' via get_if and verify the object");
{ {
auto l = get_if<line>(&x); auto l = get_if<line>(&x);
CAF_CHECK_NOT_EQUAL(l, none); if (CAF_CHECK_NOT_EQUAL(l, none))
if (l)
CAF_CHECK_EQUAL(*l, (line{{1, 2, 3}, {10, 20, 30}})); CAF_CHECK_EQUAL(*l, (line{{1, 2, 3}, {10, 20, 30}}));
} }
CAF_MESSAGE("parse config value directly from string input");
{
auto val = config_value::parse("{p1{x=1,y=2,z=3},p2{x=10,y=20,z=30}}");
CAF_CHECK(val);
if (val) {
auto l = get_if<line>(std::addressof(*val));
if (CAF_CHECK_NOT_EQUAL(l, none))
CAF_CHECK_EQUAL(*l, (line{{1, 2, 3}, {10, 20, 30}}));
}
}
} }
...@@ -108,7 +108,7 @@ public: ...@@ -108,7 +108,7 @@ public:
// nop // nop
} }
const std::string& foo() { const std::string& foo() const {
return foo_; return foo_;
} }
...@@ -116,7 +116,7 @@ public: ...@@ -116,7 +116,7 @@ public:
foo_ = std::move(value); foo_ = std::move(value);
} }
const std::string& bar() { const std::string& bar() const {
return bar_; return bar_;
} }
...@@ -129,6 +129,10 @@ private: ...@@ -129,6 +129,10 @@ private:
std::string bar_; std::string bar_;
}; };
[[maybe_unused]] bool operator==(const foobar& x, const foobar& y) {
return std::tie(x.foo(), x.bar()) == std::tie(y.foo(), y.bar());
}
template <class Inspector> template <class Inspector>
bool inspect(Inspector& f, foobar& x) { bool inspect(Inspector& f, foobar& x) {
auto get_foo = [&x]() -> decltype(auto) { return x.foo(); }; auto get_foo = [&x]() -> decltype(auto) { return x.foo(); };
......
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