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

Fix handling of CLI arguments

parent 844ad3db
......@@ -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
// -- SumType access of dictionary values --------------------------------------
......@@ -932,6 +891,68 @@ config_value make_config_value_list(Ts&&... xs) {
// -- 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 <>
struct variant_inspector_traits<config_value> {
using value_type = config_value;
......
......@@ -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, config_value& x);
// -- variadic utility ---------------------------------------------------------
template <class... Ts>
......
......@@ -18,9 +18,11 @@
#include "caf/detail/parse.hpp"
#include "caf/detail/config_consumer.hpp"
#include "caf/detail/consumer.hpp"
#include "caf/detail/parser/chars.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_ipv4_address.hpp"
#include "caf/detail/parser/read_ipv6_address.hpp"
......@@ -161,6 +163,21 @@ void parse(string_parser_state& ps, uri& x) {
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)
void parse(string_parser_state& ps, ipv4_subnet& x) {
......
......@@ -18,15 +18,15 @@
#define CAF_SUITE config_option_set
#include "caf/config_option_set.hpp"
#include "core-test.hpp"
#include "inspector-tests.hpp"
#include <map>
#include <string>
#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/settings.hpp"
......@@ -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()
......@@ -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");
{
auto l = get_if<line>(&x);
CAF_CHECK_NOT_EQUAL(l, none);
if (l)
if (CAF_CHECK_NOT_EQUAL(l, none))
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:
// nop
}
const std::string& foo() {
const std::string& foo() const {
return foo_;
}
......@@ -116,7 +116,7 @@ public:
foo_ = std::move(value);
}
const std::string& bar() {
const std::string& bar() const {
return bar_;
}
......@@ -129,6 +129,10 @@ private:
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>
bool inspect(Inspector& f, foobar& x) {
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