mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-11 07:10:36 +00:00
Add "undeclared value" handler to Configuration
This commit is contained in:
parent
e9708a5d1c
commit
9a1b7b20de
@ -36,12 +36,51 @@ Configuration::defineOption(ConfigDefinition_ptr def)
|
|||||||
Configuration&
|
Configuration&
|
||||||
Configuration::addConfigValue(string_view section, string_view name, string_view value)
|
Configuration::addConfigValue(string_view section, string_view name, string_view value)
|
||||||
{
|
{
|
||||||
ConfigDefinition_ptr& definition = lookupDefinitionOrThrow(section, name);
|
auto secItr = m_definitions.find(std::string(section));
|
||||||
|
if (secItr == m_definitions.end())
|
||||||
|
{
|
||||||
|
// fallback to undeclared handler if available
|
||||||
|
auto undItr = m_undeclaredHandlers.find(std::string(section));
|
||||||
|
if (undItr == m_undeclaredHandlers.end())
|
||||||
|
throw std::invalid_argument(stringify("no declared section [", section, "]"));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto& handler = undItr->second;
|
||||||
|
handler(section, name, value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// section was valid, get definition by name
|
||||||
|
auto& sectionDefinitions = secItr->second;
|
||||||
|
auto defItr = sectionDefinitions.find(std::string(name));
|
||||||
|
if (defItr == sectionDefinitions.end())
|
||||||
|
throw std::invalid_argument(stringify("no declared option [", section, "]:", name));
|
||||||
|
|
||||||
|
ConfigDefinition_ptr& definition = defItr->second;
|
||||||
definition->parseValue(std::string(value));
|
definition->parseValue(std::string(value));
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Configuration::addUndeclaredHandler(const std::string& section, UndeclaredValueHandler handler)
|
||||||
|
{
|
||||||
|
auto itr = m_undeclaredHandlers.find(section);
|
||||||
|
if (itr != m_undeclaredHandlers.end())
|
||||||
|
throw std::logic_error(stringify("section ", section, " already has a handler"));
|
||||||
|
|
||||||
|
m_undeclaredHandlers[section] = std::move(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Configuration::removeUndeclaredHandler(const std::string& section)
|
||||||
|
{
|
||||||
|
auto itr = m_undeclaredHandlers.find(section);
|
||||||
|
if (itr != m_undeclaredHandlers.end())
|
||||||
|
m_undeclaredHandlers.erase(itr);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Configuration::validateRequiredFields()
|
Configuration::validateRequiredFields()
|
||||||
{
|
{
|
||||||
@ -110,7 +149,7 @@ Configuration::lookupDefinitionOrThrow(string_view section, string_view name) co
|
|||||||
{
|
{
|
||||||
const auto sectionItr = m_definitions.find(std::string(section));
|
const auto sectionItr = m_definitions.find(std::string(section));
|
||||||
if (sectionItr == m_definitions.end())
|
if (sectionItr == m_definitions.end())
|
||||||
throw std::invalid_argument(stringify("No config section ", section));
|
throw std::invalid_argument(stringify("No config section [", section, "]"));
|
||||||
|
|
||||||
auto& sectionDefinitions = sectionItr->second;
|
auto& sectionDefinitions = sectionItr->second;
|
||||||
const auto definitionItr = sectionDefinitions.find(std::string(name));
|
const auto definitionItr = sectionDefinitions.find(std::string(name));
|
||||||
|
@ -177,6 +177,9 @@ namespace llarp
|
|||||||
std::function<void(T)> acceptor;
|
std::function<void(T)> acceptor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using UndeclaredValueHandler
|
||||||
|
= std::function<void(string_view section, string_view name, string_view value)>;
|
||||||
|
|
||||||
|
|
||||||
using ConfigDefinition_ptr = std::unique_ptr<ConfigDefinitionBase>;
|
using ConfigDefinition_ptr = std::unique_ptr<ConfigDefinitionBase>;
|
||||||
|
|
||||||
@ -201,7 +204,6 @@ namespace llarp
|
|||||||
/// with defaults and optionally fields which have a specified value (values provided through
|
/// with defaults and optionally fields which have a specified value (values provided through
|
||||||
/// calls to addConfigValue()).
|
/// calls to addConfigValue()).
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
SectionMap m_definitions;
|
|
||||||
|
|
||||||
/// Spefify the parameters and type of a configuration option. The parameters are members of
|
/// Spefify the parameters and type of a configuration option. The parameters are members of
|
||||||
/// ConfigDefinitionBase; the type is inferred from ConfigDefinition's template parameter T.
|
/// ConfigDefinitionBase; the type is inferred from ConfigDefinition's template parameter T.
|
||||||
@ -264,6 +266,25 @@ namespace llarp
|
|||||||
return derived->getValue();
|
return derived->getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add an "undeclared" handler for the given section. This is a handler that will be called
|
||||||
|
/// whenever a k:v pair is found that doesn't match a provided definition.
|
||||||
|
///
|
||||||
|
/// Any exception thrown by the handler will progagate back through the call to
|
||||||
|
/// addConfigValue().
|
||||||
|
///
|
||||||
|
/// @param section is the section for which any undeclared values will invoke the provided
|
||||||
|
/// handler
|
||||||
|
/// @param handler
|
||||||
|
/// @throws if there is already a handler for this section
|
||||||
|
void
|
||||||
|
addUndeclaredHandler(const std::string& section, UndeclaredValueHandler handler);
|
||||||
|
|
||||||
|
/// Removes an "undeclared" handler for the given section.
|
||||||
|
///
|
||||||
|
/// @param section is the section which we want to remove the handler for
|
||||||
|
void
|
||||||
|
removeUndeclaredHandler(const std::string& section);
|
||||||
|
|
||||||
/// Validate that all required fields are present.
|
/// Validate that all required fields are present.
|
||||||
///
|
///
|
||||||
/// @throws std::invalid_argument if configuration constraints are not met
|
/// @throws std::invalid_argument if configuration constraints are not met
|
||||||
@ -303,6 +324,10 @@ namespace llarp
|
|||||||
using DefVisitor = std::function<void(const std::string&, const ConfigDefinition_ptr&)>;
|
using DefVisitor = std::function<void(const std::string&, const ConfigDefinition_ptr&)>;
|
||||||
void visitDefinitions(const std::string& section, DefVisitor visitor) const;
|
void visitDefinitions(const std::string& section, DefVisitor visitor) const;
|
||||||
|
|
||||||
|
SectionMap m_definitions;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, UndeclaredValueHandler> m_undeclaredHandlers;
|
||||||
|
|
||||||
// track insertion order
|
// track insertion order
|
||||||
std::vector<std::string> m_sectionOrdering;
|
std::vector<std::string> m_sectionOrdering;
|
||||||
std::unordered_map<std::string, std::vector<std::string>> m_definitionOrdering;
|
std::unordered_map<std::string, std::vector<std::string>> m_definitionOrdering;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#include <config/definition.hpp>
|
#include <config/definition.hpp>
|
||||||
|
#include <util/string_view.hpp>
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
using llarp::string_view;
|
||||||
|
|
||||||
TEST_CASE("ConfigDefinition int parse test", "[config]")
|
TEST_CASE("ConfigDefinition int parse test", "[config]")
|
||||||
{
|
{
|
||||||
llarp::ConfigDefinition<int> def("foo", "bar", false, 42);
|
llarp::ConfigDefinition<int> def("foo", "bar", false, 42);
|
||||||
@ -214,3 +217,99 @@ TEST_CASE("Configuration defineOptions passthrough test", "[config]")
|
|||||||
config.defineOption<int>("foo", "bar", false, 1);
|
config.defineOption<int>("foo", "bar", false, 1);
|
||||||
CHECK(config.getConfigValue<int>("foo", "bar") == 1);
|
CHECK(config.getConfigValue<int>("foo", "bar") == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Configuration undeclared definition basic test", "[config]")
|
||||||
|
{
|
||||||
|
llarp::Configuration config;
|
||||||
|
|
||||||
|
bool invoked = false;
|
||||||
|
|
||||||
|
config.addUndeclaredHandler("foo", [&](string_view section, string_view name, string_view value) {
|
||||||
|
CHECK(section == "foo");
|
||||||
|
CHECK(name == "bar");
|
||||||
|
CHECK(value == "val");
|
||||||
|
|
||||||
|
invoked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(config.addConfigValue("foo", "bar", "val"));
|
||||||
|
|
||||||
|
CHECK(invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Configuration undeclared add more than once test", "[config]")
|
||||||
|
{
|
||||||
|
llarp::Configuration config;
|
||||||
|
|
||||||
|
std::string calledBy = "";
|
||||||
|
|
||||||
|
config.addUndeclaredHandler("foo", [&](string_view, string_view, string_view) {
|
||||||
|
calledBy = "a";
|
||||||
|
});
|
||||||
|
REQUIRE_THROWS_WITH(
|
||||||
|
config.addUndeclaredHandler("foo", [&](string_view, string_view, string_view) {
|
||||||
|
calledBy = "b";
|
||||||
|
}),
|
||||||
|
"section foo already has a handler");
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(config.addConfigValue("foo", "bar", "val"));
|
||||||
|
|
||||||
|
CHECK(calledBy == "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Configuration undeclared add/remove test", "[config]")
|
||||||
|
{
|
||||||
|
llarp::Configuration config;
|
||||||
|
|
||||||
|
std::string calledBy = "";
|
||||||
|
|
||||||
|
// add...
|
||||||
|
REQUIRE_NOTHROW(config.addUndeclaredHandler("foo", [&](string_view, string_view, string_view) {
|
||||||
|
calledBy = "a";
|
||||||
|
}));
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(config.addConfigValue("foo", "bar", "val"));
|
||||||
|
|
||||||
|
CHECK(calledBy == "a");
|
||||||
|
|
||||||
|
calledBy = "";
|
||||||
|
|
||||||
|
// ...then remove...
|
||||||
|
REQUIRE_NOTHROW(config.removeUndeclaredHandler("foo"));
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH(
|
||||||
|
config.addConfigValue("foo", "bar", "val"),
|
||||||
|
"no declared section [foo]");
|
||||||
|
|
||||||
|
// ...then add again
|
||||||
|
REQUIRE_NOTHROW(config.addUndeclaredHandler("foo", [&](string_view, string_view, string_view) {
|
||||||
|
calledBy = "b";
|
||||||
|
}));
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(config.addConfigValue("foo", "bar", "val"));
|
||||||
|
|
||||||
|
CHECK(calledBy == "b");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Configuration undeclared handler exception propagation test", "[config]")
|
||||||
|
{
|
||||||
|
llarp::Configuration config;
|
||||||
|
|
||||||
|
config.addUndeclaredHandler("foo", [](string_view, string_view, string_view) {
|
||||||
|
throw std::runtime_error("FAIL");
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE_THROWS_WITH(config.addConfigValue("foo", "bar", "val"), "FAIL");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Configuration undeclared handler wrong section", "[config]")
|
||||||
|
{
|
||||||
|
llarp::Configuration config;
|
||||||
|
|
||||||
|
config.addUndeclaredHandler("foo", [](string_view, string_view, string_view) {
|
||||||
|
throw std::runtime_error("FAIL");
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE_THROWS_WITH(config.addConfigValue("argle", "bar", "val"), "no declared section [argle]");
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user