Unverified Commit 12bcfbc7 authored by Noir's avatar Noir Committed by GitHub

Merge pull request #1314

Fix handling of the global category in settings
parents 46a747ac 4d830beb
......@@ -27,6 +27,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
destructor of the promise now checks for this case.
- Fix OpenSSL 3.0 warnings when building the OpenSSL module by switching to
newer EC-curve API.
- When working with settings, `put`, `put_missing`, `get_if`, etc. now
gracefully handle the `global` category when explicitly using it.
### Changed
......
......@@ -81,12 +81,7 @@ expected<T> get_as(const settings& xs, string_view name) {
}
/// @private
CAF_CORE_EXPORT config_value& put_impl(settings& dict,
const std::vector<string_view>& path,
config_value& value);
/// @private
CAF_CORE_EXPORT config_value& put_impl(settings& dict, string_view key,
CAF_CORE_EXPORT config_value& put_impl(settings& dict, string_view name,
config_value& value);
/// Converts `value` to a `config_value` and assigns it to `key`.
......
......@@ -11,22 +11,32 @@ namespace caf {
// note: to_string is implemented in config_value.cpp
const config_value* get_if(const settings* xs, string_view name) {
// Access the key directly unless the user specified a dot-separated path.
auto pos = name.find('.');
if (pos == std::string::npos) {
auto i = xs->find(name);
if (i == xs->end())
// The 'global' category is special in the sense that it refers back to the
// root. Somewhat like '::foo' in C++. This means we can just drop it here.
using namespace caf::literals;
auto gl = "global."_sv;
if (starts_with(name, gl))
name.remove_prefix(gl.size());
// Climb down the tree. In each step, we resolve `xs` and `name` to the next
// level until there is no category left to resolve. At that point it's a
// trivial name lookup.
for (;;) {
if (auto pos = name.find('.'); pos == std::string::npos) {
if (auto i = xs->find(name); i == xs->end())
return nullptr;
// We can't simply return the result here, because it might be a pointer.
else
return &i->second;
}
// We're dealing with a `<category>.<key>`-formatted string, extract the
// sub-settings by category and recurse.
auto i = xs->find(name.substr(0, pos));
if (i == xs->end() || !holds_alternative<config_value::dictionary>(i->second))
} else {
auto category = name.substr(0, pos);
name.remove_prefix(pos + 1);
if (auto i = xs->find(category);
i == xs->end() || !holds_alternative<settings>(i->second)) {
return nullptr;
return get_if(&get<config_value::dictionary>(i->second),
name.substr(pos + 1));
} else {
xs = std::addressof(get<settings>(i->second));
}
}
}
}
expected<std::string> get_or(const settings& xs, string_view name,
......@@ -41,8 +51,7 @@ config_value& put_impl(settings& dict, const std::vector<string_view>& path,
config_value& value) {
// Sanity check.
CAF_ASSERT(!path.empty());
// TODO: We implicitly swallow the `global.` suffix as a hotfix, but we
// actually should drop `global.` on the upper layers.
// Like in get_if: we always drop a 'global.' suffix.
if (path.front() == "global") {
std::vector<string_view> new_path{path.begin() + 1, path.end()};
return put_impl(dict, new_path, value);
......@@ -67,10 +76,33 @@ config_value& put_impl(settings& dict, const std::vector<string_view>& path,
return iter->second;
}
config_value& put_impl(settings& dict, string_view key, config_value& value) {
std::vector<string_view> path;
split(path, key, ".");
return put_impl(dict, path, value);
config_value& put_impl(settings& dict, string_view name, config_value& value) {
// Like in get_if: we always drop a 'global.' suffix.
using namespace caf::literals;
auto gl = "global."_sv;
if (starts_with(name, gl))
name.remove_prefix(gl.size());
// Climb down the tree, similar to get_if. Only this time, we create the
// necessary structure as we go until there is no category left to resolve. At
// that point it's a trivial insertion (override).
auto xs = &dict;
for (;;) {
if (auto pos = name.find('.'); pos == string_view::npos) {
return xs->insert_or_assign(name, std::move(value)).first->second;
} else {
auto category = name.substr(0, pos);
name.remove_prefix(pos + 1);
if (auto i = xs->find(category); i == xs->end()) {
auto j = xs->emplace(category, settings{}).first;
xs = std::addressof(get<settings>(j->second));
} else if (!holds_alternative<settings>(i->second)) {
i->second = settings{};
xs = std::addressof(get<settings>(i->second));
} else {
xs = std::addressof(get<settings>(i->second));
}
}
}
}
config_value::list& put_list(settings& xs, std::string name) {
......
......@@ -171,4 +171,28 @@ CAF_TEST(read_config accepts the to_string output of settings) {
CHECK_EQ(x, y);
}
SCENARIO("put_missing normalizes 'global' suffixes") {
GIVEN("empty settings") {
settings uut;
WHEN("calling put_missing with and without 'global' suffix") {
THEN("put_missing drops the 'global' suffix") {
put_missing(uut, "global.foo", "bar"s);
CHECK_EQ(get_as<std::string>(uut, "foo"), "bar"s);
CHECK_EQ(get_as<std::string>(uut, "global.foo"), "bar"s);
}
}
}
GIVEN("settings with a value for 'foo'") {
settings uut;
uut["foo"] = "bar"s;
WHEN("calling put_missing 'global.foo'") {
THEN("the function call results in a no-op") {
put_missing(uut, "global.foo", "baz"s);
CHECK_EQ(get_as<std::string>(uut, "foo"), "bar"s);
CHECK_EQ(get_as<std::string>(uut, "global.foo"), "bar"s);
}
}
}
}
END_FIXTURE_SCOPE()
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