Commit 5a94d7ba authored by Matthias Vallentin's avatar Matthias Vallentin

Merge branch 'topic/drop-global-category'

* origin/topic/drop-global-category:
  Fix build on Clang 3.6
  Allow users to omit [global] in config file
  Drop the implicit global category
parents bff39e81 f384834e
...@@ -361,12 +361,19 @@ T get(const actor_system_config& cfg, string_view name) { ...@@ -361,12 +361,19 @@ T get(const actor_system_config& cfg, string_view name) {
/// Retrieves the value associated to `name` from `cfg` or returns /// Retrieves the value associated to `name` from `cfg` or returns
/// `default_value`. /// `default_value`.
/// @relates config_value /// @relates config_value
template <class K, class V> template <class T, class = typename std::enable_if<
auto get_or(const actor_system_config& cfg, K&& name, V&& default_value) !std::is_pointer<T>::value
-> decltype(get_or(content(cfg), std::forward<K>(name), && !std::is_convertible<T, string_view>::value>::type>
std::forward<V>(default_value))) { T get_or(const actor_system_config& cfg, string_view name, T default_value) {
return get_or(content(cfg), std::forward<K>(name), return get_or(content(cfg), name, std::move(default_value));
std::forward<V>(default_value)); }
/// Retrieves the value associated to `name` from `cfg` or returns
/// `default_value`.
/// @relates config_value
inline std::string get_or(const actor_system_config& cfg, string_view name,
string_view default_value) {
return get_or(content(cfg), name, default_value);
} }
} // namespace caf } // namespace caf
...@@ -239,7 +239,7 @@ void read_ini_section(state<Iterator, Sentinel>& ps, Consumer&& consumer) { ...@@ -239,7 +239,7 @@ void read_ini_section(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) { void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
using std::swap; using std::swap;
std::string tmp; std::string tmp{"global"};
auto alnum_or_dash = [](char x) { auto alnum_or_dash = [](char x) {
return isalnum(x) || x == '-' || x == '_'; return isalnum(x) || x == '-' || x == '_';
}; };
...@@ -255,6 +255,7 @@ void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) { ...@@ -255,6 +255,7 @@ void read_ini(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
transition(init, " \t\n") transition(init, " \t\n")
fsm_epsilon(read_ini_comment(ps, consumer), init, ';') fsm_epsilon(read_ini_comment(ps, consumer), init, ';')
transition(start_section, '[') transition(start_section, '[')
fsm_epsilon_if(tmp == "global", read_ini_section(ps, begin_section()), init)
} }
// Read the section key after reading an '['. // Read the section key after reading an '['.
state(start_section) { state(start_section) {
......
...@@ -302,7 +302,9 @@ actor_system_config& actor_system_config::set_impl(string_view name, ...@@ -302,7 +302,9 @@ actor_system_config& actor_system_config::set_impl(string_view name,
auto opt = custom_options_.qualified_name_lookup(name); auto opt = custom_options_.qualified_name_lookup(name);
if (opt != nullptr && opt->check(value) == none) { if (opt != nullptr && opt->check(value) == none) {
opt->store(value); opt->store(value);
auto& dict = content[opt->category()].as_dictionary(); auto category = opt->category();
auto& dict = category == "global" ? content
: content[category].as_dictionary();
dict[opt->long_name()] = std::move(value); dict[opt->long_name()] = std::move(value);
} }
return *this; return *this;
......
...@@ -124,17 +124,15 @@ auto config_option_set::parse(settings& config, argument_iterator first, ...@@ -124,17 +124,15 @@ auto config_option_set::parse(settings& config, argument_iterator first,
auto opt_name = opt.long_name(); auto opt_name = opt.long_name();
auto opt_ctg = opt.category(); auto opt_ctg = opt.category();
// Try inserting a new submap into the config or fill existing one. // Try inserting a new submap into the config or fill existing one.
auto& entry = config[opt_ctg]; auto& entry = opt_ctg == "global" ? config
if (!holds_alternative<config_value::dictionary>(entry)) : config[opt_ctg].as_dictionary();
entry = config_value::dictionary{};
auto& submap = get<config_value::dictionary>(entry);
// Flags only consume the current element. // Flags only consume the current element.
if (opt.is_flag()) { if (opt.is_flag()) {
if (arg_begin != arg_end) if (arg_begin != arg_end)
return pec::illegal_argument; return pec::illegal_argument;
config_value cfg_true{true}; config_value cfg_true{true};
opt.store(cfg_true); opt.store(cfg_true);
submap[opt_name] = cfg_true; entry[opt_name] = cfg_true;
} else { } else {
if (arg_begin == arg_end) if (arg_begin == arg_end)
return pec::missing_argument; return pec::missing_argument;
...@@ -158,7 +156,7 @@ auto config_option_set::parse(settings& config, argument_iterator first, ...@@ -158,7 +156,7 @@ auto config_option_set::parse(settings& config, argument_iterator first,
} }
} }
opt.store(*val); opt.store(*val);
submap[opt_name] = std::move(*val); entry[opt_name] = std::move(*val);
} }
return pec::success; return pec::success;
}; };
......
...@@ -155,7 +155,8 @@ ini_consumer* ini_category_consumer::dparent() { ...@@ -155,7 +155,8 @@ ini_consumer* ini_category_consumer::dparent() {
ini_consumer::ini_consumer(config_option_set& options, settings& cfg) ini_consumer::ini_consumer(config_option_set& options, settings& cfg)
: options_(options), : options_(options),
cfg_(cfg) { cfg_(cfg),
current_key("global") {
// nop // nop
} }
...@@ -170,13 +171,18 @@ void ini_consumer::key(std::string name) { ...@@ -170,13 +171,18 @@ void ini_consumer::key(std::string name) {
void ini_consumer::value_impl(config_value&& x) { void ini_consumer::value_impl(config_value&& x) {
using dict_type = config_value::dictionary; using dict_type = config_value::dictionary;
auto dict = get_if<dict_type>(&x); auto dict = get_if<dict_type>(&x);
auto& dst = cfg_.emplace(current_key, dict_type{}).first->second; if (current_key != "global") {
if (dict != nullptr && !dict->empty() && holds_alternative<dict_type>(dst)) { auto& dst = cfg_.emplace(current_key, dict_type{}).first->second;
auto& dst_dict = get<dict_type>(dst); if (dict != nullptr && !dict->empty() && holds_alternative<dict_type>(dst)) {
// We need to "merge" values into the destination, because it can already auto& dst_dict = get<dict_type>(dst);
// contain any number of unrelated entries. // We need to "merge" values into the destination, because it can already
// contain any number of unrelated entries.
for (auto& entry : *dict)
dst_dict.insert_or_assign(entry.first, std::move(entry.second));
}
} else {
for (auto& entry : *dict) for (auto& entry : *dict)
dst_dict.insert_or_assign(entry.first, std::move(entry.second)); cfg_.insert_or_assign(entry.first, std::move(entry.second));
} }
} }
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include "caf/settings.hpp" #include "caf/settings.hpp"
#include "caf/string_algorithms.hpp"
namespace caf { namespace caf {
std::string get_or(const settings& xs, string_view name, std::string get_or(const settings& xs, string_view name,
...@@ -32,6 +34,12 @@ config_value& put_impl(settings& dict, const std::vector<string_view>& path, ...@@ -32,6 +34,12 @@ config_value& put_impl(settings& dict, const std::vector<string_view>& path,
config_value& value) { config_value& value) {
// Sanity check. // Sanity check.
CAF_ASSERT(!path.empty()); CAF_ASSERT(!path.empty());
// TODO: We implicitly swallow the `global.` suffix as a hotfix, but we
// actually should drop `global.` on the upper layers.
if (path.front() == "global") {
std::vector<string_view> new_path{path.begin() + 1, path.end()};
return put_impl(dict, new_path, value);
}
// Navigate path. // Navigate path.
auto last = path.end(); auto last = path.end();
auto back = last - 1; auto back = last - 1;
......
...@@ -101,18 +101,18 @@ CAF_TEST(parse with ref syncing) { ...@@ -101,18 +101,18 @@ CAF_TEST(parse with ref syncing) {
CAF_CHECK_EQUAL(get<int>(cfg, "foo.i"), 42); CAF_CHECK_EQUAL(get<int>(cfg, "foo.i"), 42);
} }
CAF_TEST(implicit global) { CAF_TEST(drop global) {
opts.add<int>("value", "some value").add<bool>("help", "print help text"); opts.add<int>("value", "some value").add<bool>("help", "print help text");
CAF_MESSAGE("test long option with argument"); CAF_MESSAGE("test long option with argument");
settings cfg; settings cfg;
auto res = opts.parse(cfg, {"--value=42"}); auto res = opts.parse(cfg, {"--value=42"});
CAF_CHECK_EQUAL(res.first, pec::success); CAF_CHECK_EQUAL(res.first, pec::success);
CAF_CHECK_EQUAL(get_if<int>(&cfg, "global.value"), 42); CAF_CHECK_EQUAL(get_if<int>(&cfg, "value"), 42);
CAF_MESSAGE("test long option flag"); CAF_MESSAGE("test long option flag");
cfg.clear(); cfg.clear();
res = opts.parse(cfg, {"--help"}); res = opts.parse(cfg, {"--help"});
CAF_CHECK_EQUAL(res.first, pec::success); CAF_CHECK_EQUAL(res.first, pec::success);
CAF_CHECK_EQUAL(get_or(cfg, "global.help", false), true); CAF_CHECK_EQUAL(get_or(cfg, "help", false), true);
} }
CAF_TEST(atom parameters) { CAF_TEST(atom parameters) {
...@@ -123,7 +123,7 @@ CAF_TEST(atom parameters) { ...@@ -123,7 +123,7 @@ CAF_TEST(atom parameters) {
auto res = opts.parse(cfg, std::move(args)); auto res = opts.parse(cfg, std::move(args));
if (res.first != pec::success) if (res.first != pec::success)
return res.first; return res.first;
auto atm = get_if<atom_value>(&cfg, "global.value"); auto atm = get_if<atom_value>(&cfg, "value");
if (atm == none) if (atm == none)
return sec::invalid_argument; return sec::invalid_argument;
return *atm; return *atm;
......
...@@ -34,7 +34,6 @@ using ls = std::vector<std::string>; ...@@ -34,7 +34,6 @@ using ls = std::vector<std::string>;
namespace { namespace {
const char test_ini[] = R"( const char test_ini[] = R"(
[global]
is_server=true is_server=true
port=4242 port=4242
nodes=["sun", "venus", ] nodes=["sun", "venus", ]
...@@ -45,6 +44,19 @@ file-name = "foobar.ini" ; our file name ...@@ -45,6 +44,19 @@ file-name = "foobar.ini" ; our file name
impl = 'foo';some atom impl = 'foo';some atom
)"; )";
const char test_ini2[] = R"(
is_server = true
port = 4242
nodes = ["sun", "venus"]
logger = {
file-name = "foobar.ini"
}
scheduler = {
timing = 2us,
impl = 'foo'
}
)";
struct fixture { struct fixture {
detail::parser::state<std::string::const_iterator> res; detail::parser::state<std::string::const_iterator> res;
config_option_set options; config_option_set options;
...@@ -82,12 +94,35 @@ CAF_TEST(ini_consumer) { ...@@ -82,12 +94,35 @@ CAF_TEST(ini_consumer) {
res.e = str.end(); res.e = str.end();
detail::parser::read_ini(res, consumer); detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success); CAF_CHECK_EQUAL(res.code, pec::success);
CAF_CHECK_EQUAL(get<bool>(config, "global.is_server"), true); CAF_CHECK_EQUAL(get<bool>(config, "is_server"), true);
CAF_CHECK_EQUAL(get<uint16_t>(config, "global.port"), 4242u); CAF_CHECK_EQUAL(get<uint16_t>(config, "port"), 4242u);
CAF_CHECK_EQUAL(get<ls>(config, "global.nodes"), ls({"sun", "venus"})); CAF_CHECK_EQUAL(get<ls>(config, "nodes"), ls({"sun", "venus"}));
CAF_CHECK_EQUAL(get<string>(config, "logger.file-name"), "foobar.ini"); CAF_CHECK_EQUAL(get<string>(config, "logger.file-name"), "foobar.ini");
CAF_CHECK_EQUAL(get<timespan>(config, "scheduler.timing"), timespan(2000)); CAF_CHECK_EQUAL(get<timespan>(config, "scheduler.timing"), timespan(2000));
CAF_CHECK_EQUAL(get<atom_value>(config, "scheduler.impl"), atom("foo")); CAF_CHECK_EQUAL(get<atom_value>(config, "scheduler.impl"), atom("foo"));
} }
CAF_TEST(simplified syntax) {
std::string str = test_ini;
CAF_MESSAGE("read test_ini");
{
detail::ini_consumer consumer{options, config};
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
}
str = test_ini2;
settings config2;
CAF_MESSAGE("read test_ini2");
{
detail::ini_consumer consumer{options, config2};
res.i = str.begin();
res.e = str.end();
detail::parser::read_ini(res, consumer);
CAF_CHECK_EQUAL(res.code, pec::success);
}
CAF_CHECK_EQUAL(config, config2);
}
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