diff --git a/llarp/config/definition.cpp b/llarp/config/definition.cpp index aae33bb82..af9d907f6 100644 --- a/llarp/config/definition.cpp +++ b/llarp/config/definition.cpp @@ -16,6 +16,18 @@ namespace llarp { } + template <> + bool + OptionDefinition::fromString(const std::string& input) + { + if (input == "false" || input == "off" || input == "0" || input == "no") + return false; + else if (input == "true" || input == "on" || input == "1" || input == "yes") + return true; + else + throw std::invalid_argument(stringify(input, " is not a valid bool")); + } + ConfigDefinition& ConfigDefinition::defineOption(OptionDefinition_ptr def) { diff --git a/llarp/config/definition.hpp b/llarp/config/definition.hpp index fc88291d6..d72b23e66 100644 --- a/llarp/config/definition.hpp +++ b/llarp/config/definition.hpp @@ -168,17 +168,19 @@ namespace llarp stringify("duplicate value for ", name, ", previous value: ", parsedValues[0])); } + parsedValues.emplace_back(fromString(input)); + } + + T + fromString(const std::string& input) + { std::istringstream iss(input); T t; iss >> t; if (iss.fail()) - { throw std::invalid_argument(stringify(input, " is not a valid ", typeid(T).name())); - } else - { - parsedValues.emplace_back(std::move(t)); - } + return t; } std::string @@ -239,6 +241,12 @@ namespace llarp std::function acceptor; }; + /// Specialization for bool types. We don't want to use stringstream parsing in this + /// case because we want to accept "truthy" and "falsy" string values (e.g. "off" == false) + template <> + bool + OptionDefinition::fromString(const std::string& input); + using UndeclaredValueHandler = std::function; diff --git a/test/config/test_llarp_config_definition.cpp b/test/config/test_llarp_config_definition.cpp index 44792fd64..279bd5e72 100644 --- a/test/config/test_llarp_config_definition.cpp +++ b/test/config/test_llarp_config_definition.cpp @@ -387,3 +387,19 @@ TEST_CASE("ConfigDefinition [bind]iface regression", "[config regression]") CHECK(undeclaredName == "enp35s0"); CHECK(undeclaredValue == "1091"); } + +TEST_CASE("ConfigDefinition truthy bool values", "[config]") +{ + llarp::OptionDefinition def("foo", "bar", false, true); + + // defaults to true + auto maybe = def.getValue(); + CHECK(maybe.has_value()); + CHECK(maybe.value() == true); + + // "off" should result in false + CHECK_NOTHROW(def.parseValue("off")); + maybe = def.getValue(); + CHECK(maybe.has_value()); + CHECK(maybe.value() == false); +}