Commit 574d4346 authored by Dominik Charousset's avatar Dominik Charousset

Revert "Remove dead code"

This reverts commit 0392e327. The
`config_value::parse` is unused in the main repository, but the
incubator still uses it.
parent 0392e327
......@@ -104,6 +104,15 @@ public:
~config_value();
// -- parsing ----------------------------------------------------------------
/// Tries to parse a value from `str`.
static expected<config_value> parse(string_view::iterator first,
string_view::iterator last);
/// Tries to parse a value from `str`.
static expected<config_value> parse(string_view str);
// -- properties -------------------------------------------------------------
/// Converts the value to a list with one element. Does nothing if the value
......
......@@ -53,6 +53,44 @@ config_value::~config_value() {
// nop
}
// -- parsing ------------------------------------------------------------------
expected<config_value> config_value::parse(string_view::iterator first,
string_view::iterator last) {
using namespace detail;
auto i = first;
// Sanity check.
if (i == last)
return make_error(pec::unexpected_eof);
// Skip to beginning of the argument.
while (isspace(*i))
if (++i == last)
return make_error(pec::unexpected_eof);
// Dispatch to parser.
detail::config_value_consumer f;
string_parser_state res{i, last};
parser::read_config_value(res, f);
if (res.code == pec::success)
return std::move(f.result);
// Assume an unescaped string unless the first character clearly indicates
// otherwise.
switch (*i) {
case '[':
case '{':
case '"':
case '\'':
return make_error(res.code);
default:
if (isdigit(*i))
return make_error(res.code);
return config_value{std::string{first, last}};
}
}
expected<config_value> config_value::parse(string_view str) {
return parse(str.begin(), str.end());
}
// -- properties ---------------------------------------------------------------
void config_value::convert_to_list() {
......
......@@ -224,6 +224,130 @@ CAF_TEST(heterogeneous dictionary) {
CAF_CHECK_EQUAL(get<string_list>(xs, "nodes.preload"), nodes);
}
CAF_TEST(successful parsing) {
// Store the parsed value on the stack, because the unit test framework takes
// references when comparing values. Since we call get<T>() on the result of
// parse(), we would end up with a reference to a temporary.
config_value parsed;
auto parse = [&](const string& str) -> config_value& {
auto x = config_value::parse(str);
if (!x)
CAF_FAIL("cannot parse " << str << ": assumed a result but error "
<< to_string(x.error()));
parsed = std::move(*x);
return parsed;
};
using di = caf::dictionary<int>; // Dictionary-of-integers.
using ls = std::vector<string>; // List-of-strings.
using li = std::vector<int>; // List-of-integers.
using lli = std::vector<li>; // List-of-list-of-integers.
using std::chrono::milliseconds;
CAF_CHECK_EQUAL(get<int64_t>(parse("123")), 123);
CAF_CHECK_EQUAL(get<int64_t>(parse("+123")), 123);
CAF_CHECK_EQUAL(get<int64_t>(parse("-1")), -1);
CAF_CHECK_EQUAL(get<double>(parse("1.")), 1.);
CAF_CHECK_EQUAL(get<string>(parse("\"abc\"")), "abc");
CAF_CHECK_EQUAL(get<string>(parse("abc")), "abc");
CAF_CHECK_EQUAL(get<li>(parse("[1, 2, 3]")), li({1, 2, 3}));
CAF_CHECK_EQUAL(get<ls>(parse("[\"abc\", \"def\", \"ghi\"]")),
ls({"abc", "def", "ghi"}));
CAF_CHECK_EQUAL(get<lli>(parse("[[1, 2], [3]]")), lli({li{1, 2}, li{3}}));
CAF_CHECK_EQUAL(get<timespan>(parse("10ms")), milliseconds(10));
CAF_CHECK_EQUAL(get<di>(parse("{a=1,b=2}")), di({{"a", 1}, {"b", 2}}));
}
#define CHECK_CLI_PARSE(type, str, ...) \
do { \
/* Note: parse_impl from make_config_option.hpp internally dispatches */ \
/* to parse_cli. No need to replicate that wrapping code here. */ \
if (auto res = caf::detail::parse_impl<type>(nullptr, str)) { \
type expected_res{__VA_ARGS__}; \
if (auto unboxed = ::caf::get_if<type>(std::addressof(*res))) { \
if (*unboxed == expected_res) \
CAF_CHECK_PASSED("parse(" << str << ") == " << expected_res); \
else \
CAF_CHECK_FAILED(*unboxed << " != " << expected_res); \
} else { \
CAF_CHECK_FAILED(*res << " != " << expected_res); \
} \
} else { \
CAF_CHECK_FAILED("parse(" << str << ") -> " << res.error()); \
} \
} while (false)
#define CHECK_CLI_PARSE_FAILS(type, str) \
do { \
if (auto res = caf::detail::parse_impl<type>(nullptr, str)) { \
CAF_CHECK_FAILED("unexpected parser result: " << *res); \
} else { \
CAF_CHECK_PASSED("parse(" << str << ") == " << res.error()); \
} \
} while (false)
CAF_TEST(parsing via parse_cli enables shortcut syntax for some types) {
using ls = std::vector<string>; // List-of-strings.
using li = std::vector<int>; // List-of-integers.
using lli = std::vector<li>; // List-of-list-of-integers.
CAF_MESSAGE("lists can omit square brackets");
CHECK_CLI_PARSE(int, "123", 123);
CHECK_CLI_PARSE(li, "[ 1,2 , 3 ,]", 1, 2, 3);
CHECK_CLI_PARSE(li, "[ 1,2 , 3 ]", 1, 2, 3);
CHECK_CLI_PARSE(li, " 1,2 , 3 ,", 1, 2, 3);
CHECK_CLI_PARSE(li, " 1,2 , 3 ", 1, 2, 3);
CHECK_CLI_PARSE(li, " [ ] ", li{});
CHECK_CLI_PARSE(li, " ", li{});
CHECK_CLI_PARSE(li, "", li{});
CHECK_CLI_PARSE(li, "[123]", 123);
CHECK_CLI_PARSE(li, "123", 123);
CAF_MESSAGE("brackets must have matching opening/closing brackets");
CHECK_CLI_PARSE_FAILS(li, " 1,2 , 3 ,]");
CHECK_CLI_PARSE_FAILS(li, " 1,2 , 3 ]");
CHECK_CLI_PARSE_FAILS(li, "123]");
CHECK_CLI_PARSE_FAILS(li, "[ 1,2 , 3 ,");
CHECK_CLI_PARSE_FAILS(li, "[ 1,2 , 3 ");
CHECK_CLI_PARSE_FAILS(li, "[123");
CAF_MESSAGE("string lists can omit quotation marks");
CHECK_CLI_PARSE(string, R"_("123")_", "123");
CHECK_CLI_PARSE(string, R"_(123)_", "123");
CHECK_CLI_PARSE(ls, R"_([ "1 ","2" , "3" ,])_", "1 ", "2", "3");
CHECK_CLI_PARSE(ls, R"_([ 1,2 , 3 ,])_", "1", "2", "3");
CHECK_CLI_PARSE(ls, R"_([ 1,2 , 3 ])_", "1", "2", "3");
CHECK_CLI_PARSE(ls, R"_( 1,2 , 3 ,)_", "1", "2", "3");
CHECK_CLI_PARSE(ls, R"_( 1,2 , 3 )_", "1", "2", "3");
CHECK_CLI_PARSE(ls, R"_( [ ] )_", ls{});
CHECK_CLI_PARSE(ls, R"_( )_", ls{});
CHECK_CLI_PARSE(ls, R"_(["abc"])_", "abc");
CHECK_CLI_PARSE(ls, R"_([abc])_", "abc");
CHECK_CLI_PARSE(ls, R"_("abc")_", "abc");
CHECK_CLI_PARSE(ls, R"_(abc)_", "abc");
CAF_MESSAGE("nested lists can omit the outer square brackets");
CHECK_CLI_PARSE(lli, "[[1, 2, 3, ], ]", li({1, 2, 3}));
CHECK_CLI_PARSE(lli, "[[1, 2, 3]]", li({1, 2, 3}));
CHECK_CLI_PARSE(lli, "[1, 2, 3, ]", li({1, 2, 3}));
CHECK_CLI_PARSE(lli, "[1, 2, 3]", li({1, 2, 3}));
CHECK_CLI_PARSE(lli, "[[1], [2]]", li({1}), li({2}));
CHECK_CLI_PARSE(lli, "[1], [2]", li({1}), li({2}));
CHECK_CLI_PARSE_FAILS(lli, "1");
CHECK_CLI_PARSE_FAILS(lli, "1, 2");
CHECK_CLI_PARSE_FAILS(lli, "[1, 2]]");
CHECK_CLI_PARSE_FAILS(lli, "[[1, 2]");
}
CAF_TEST(unsuccessful parsing) {
auto parse = [](const string& str) {
auto x = config_value::parse(str);
if (x)
CAF_FAIL("assumed an error but got a result");
return std::move(x.error());
};
CAF_CHECK_EQUAL(parse("10msb"), pec::trailing_character);
CAF_CHECK_EQUAL(parse("10foo"), pec::trailing_character);
CAF_CHECK_EQUAL(parse("[1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=,"), pec::unexpected_character);
CAF_CHECK_EQUAL(parse("{a=1,"), pec::unexpected_eof);
CAF_CHECK_EQUAL(parse("{a=1 b=2}"), pec::unexpected_character);
}
CAF_TEST(conversion to simple tuple) {
using tuple_type = std::tuple<size_t, std::string>;
config_value x{42};
......@@ -316,3 +440,71 @@ CAF_TEST(conversion to std::unordered_multimap) {
CAF_REQUIRE(ys);
CAF_CHECK_EQUAL(*ys, map_type({{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}}));
}
namespace {
struct point_3d {
int32_t x;
int32_t y;
int32_t z;
};
[[maybe_unused]] bool operator==(const point_3d& x, const point_3d& y) {
return std::tie(x.x, x.y, x.z) == std::tie(y.x, y.y, y.z);
}
template <class Inspector>
bool inspect(Inspector& f, point_3d& x) {
return f.object(x).fields(f.field("x", x.x), f.field("y", x.y),
f.field("z", x.z));
}
struct line {
point_3d p1;
point_3d p2;
};
[[maybe_unused]] bool operator==(const line& x, const line& y) {
return std::tie(x.p1, x.p2) == std::tie(y.p1, y.p2);
}
template <class Inspector>
bool inspect(Inspector& f, line& x) {
return f.object(x).fields(f.field("p1", x.p1), f.field("p2", x.p2));
}
} // namespace
CAF_TEST(config values pick up user defined inspect overloads) {
CAF_MESSAGE("users can fill dictionaries with field contents");
{
config_value x;
auto& dict = x.as_dictionary();
put(dict, "p1.x", 1);
put(dict, "p1.y", 2);
put(dict, "p1.z", 3);
put(dict, "p2.x", 10);
put(dict, "p2.y", 20);
put(dict, "p2.z", 30);
auto l = get_if<line>(&x);
if (CAF_CHECK_NOT_EQUAL(l, none))
CAF_CHECK_EQUAL(*l, (line{{1, 2, 3}, {10, 20, 30}}));
}
CAF_MESSAGE("users can pass objects as dictionaries on the command line");
{
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}}));
}
}
CAF_MESSAGE("value readers appear as inspectors with human-readable format");
{
config_value x{std::string{"saturday"}};
auto val = get_if<weekday>(&x);
if (CAF_CHECK(val))
CAF_CHECK_EQUAL(*val, weekday::saturday);
}
}
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