Commit 84c707d8 authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'issue/1139'

Close #1139.
parents b08139c6 0bea4d03
......@@ -24,6 +24,9 @@ is based on [Keep a Changelog](https://keepachangelog.com).
generalizing the function to `config_value` as well.
- The `typed_response_promise` received additional member functions to mirror
the interface of the untyped `response_promise`.
- Configuration files now allow dot-separated notation for keys. For example,
users may write `caf.scheduler.max-threads = 4` instead of the nested form
`caf { scheduler { max-threads = 4 } }`.
### Deprecated
......
......@@ -116,6 +116,15 @@ void read_config_map(State& ps, Consumer&& consumer) {
auto alnum_or_dash = [](char 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
start();
term_state(init) {
......@@ -131,13 +140,14 @@ void read_config_map(State& ps, Consumer&& consumer) {
// Reads a key of a "key=value" line.
state(read_key_name) {
transition(read_key_name, alnum_or_dash, key += ch)
fsm_transition(read_config_map(ps, recurse()), done, '.')
epsilon(await_assignment)
}
// Reads the assignment operator in a "key=value" line.
state(await_assignment) {
transition(await_assignment, " \t")
transition(await_value, "=:", consumer.key(std::move(key)))
epsilon(await_value, '{', consumer.key(std::move(key)))
transition(await_value, "=:", set_key())
epsilon(await_value, '{', set_key())
}
// Reads the value in a "key=value" line.
state(await_value) {
......
......@@ -297,4 +297,44 @@ CAF_TEST(basic and basic containers options) {
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()
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