Commit bc8b21fc authored by Dominik Charousset's avatar Dominik Charousset

Allow dot-separated notation in config files

parent b08139c6
...@@ -116,6 +116,15 @@ void read_config_map(State& ps, Consumer&& consumer) { ...@@ -116,6 +116,15 @@ void read_config_map(State& ps, Consumer&& consumer) {
auto alnum_or_dash = [](char x) { auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_'; return isalnum(x) || x == '-' || x == '_';
}; };
auto set_key = [&consumer, &key] {
std::string tmp;
tmp.swap(key);
consumer.key(std::move(tmp));
};
auto recurse = [&consumer, &set_key]() -> decltype(auto) {
set_key();
return consumer.begin_map();
};
// clang-format off // clang-format off
start(); start();
term_state(init) { term_state(init) {
...@@ -131,13 +140,14 @@ void read_config_map(State& ps, Consumer&& consumer) { ...@@ -131,13 +140,14 @@ void read_config_map(State& ps, Consumer&& consumer) {
// Reads a key of a "key=value" line. // Reads a key of a "key=value" line.
state(read_key_name) { state(read_key_name) {
transition(read_key_name, alnum_or_dash, key += ch) transition(read_key_name, alnum_or_dash, key += ch)
fsm_transition(read_config_map(ps, recurse()), done, '.')
epsilon(await_assignment) epsilon(await_assignment)
} }
// Reads the assignment operator in a "key=value" line. // Reads the assignment operator in a "key=value" line.
state(await_assignment) { state(await_assignment) {
transition(await_assignment, " \t") transition(await_assignment, " \t")
transition(await_value, "=:", consumer.key(std::move(key))) transition(await_value, "=:", set_key())
epsilon(await_value, '{', consumer.key(std::move(key))) epsilon(await_value, '{', set_key())
} }
// Reads the value in a "key=value" line. // Reads the value in a "key=value" line.
state(await_value) { state(await_value) {
......
...@@ -297,4 +297,44 @@ CAF_TEST(basic and basic containers options) { ...@@ -297,4 +297,44 @@ CAF_TEST(basic and basic containers options) {
string_map({{"a", "1"}, {"b", "2"}, {"c", "3"}})); string_map({{"a", "1"}, {"b", "2"}, {"c", "3"}}));
} }
SCENARIO("config files allow both nested and dot-separated values") {
GIVEN("the option my.answer.value") {
config_option_adder{cfg.custom_options(), "my.answer"}
.add<int32_t>("first", "the first answer")
.add<int32_t>("second", "the second answer");
std::vector<std::string> allowed_input_strings{
"my { answer { first = 1, second = 2 } }",
"my.answer { first = 1, second = 2 }",
"my { answer.first = 1, answer.second = 2 }",
"my.answer.first = 1, my.answer.second = 2",
"my { answer { first = 1 }, answer.second = 2 }",
"my { answer.first = 1, answer { second = 2} }",
"my.answer.first = 1, my { answer { second = 2 } }",
};
auto make_result = [] {
settings answer;
answer["first"] = 1;
answer["second"] = 2;
settings my;
my["answer"] = std::move(answer);
settings result;
result["my"] = std::move(my);
return result;
};
auto result = make_result();
for (const auto& input_string : allowed_input_strings) {
WHEN("parsing the file input '" << input_string << "'") {
std::istringstream input{input_string};
auto err = cfg.parse(string_list{}, input);
THEN("the actor system contains values for my.answer.(first|second)") {
CHECK_EQ(err, error{});
CHECK_EQ(get_or(cfg, "my.answer.first", -1), 1);
CHECK_EQ(get_or(cfg, "my.answer.second", -1), 2);
CHECK_EQ(content(cfg), result);
}
}
}
}
}
CAF_TEST_FIXTURE_SCOPE_END() 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