diff options
-rw-r--r-- | src/core/common/Argument.cpp | 51 | ||||
-rw-r--r-- | src/core/common/Argument.hpp | 11 | ||||
-rw-r--r-- | test/core/common/ArgumentTest.cpp | 24 |
3 files changed, 71 insertions, 15 deletions
diff --git a/src/core/common/Argument.cpp b/src/core/common/Argument.cpp index b10fad3..ee129a3 100644 --- a/src/core/common/Argument.cpp +++ b/src/core/common/Argument.cpp @@ -54,7 +54,8 @@ Argument Argument::Any(std::string name) Argument Argument::Any(std::string name, Variant defaultValue) { - return Argument{name, &RttiTypes::None, &RttiTypes::None, defaultValue, true}; + return Argument{name, &RttiTypes::None, &RttiTypes::None, defaultValue, + true}; } Argument Argument::Bool(std::string name) @@ -95,7 +96,8 @@ Argument Argument::String(std::string name) Argument Argument::String(std::string name, const Variant::stringType &defaultValue) { - return Argument{name, &RttiTypes::String, Variant::fromString(defaultValue)}; + return Argument{name, &RttiTypes::String, + Variant::fromString(defaultValue)}; } Argument Argument::Object(std::string name, const Rtti *type) @@ -158,7 +160,8 @@ Argument Argument::Map(std::string name, const Variant::mapType &defaultValue) Argument Argument::Map(std::string name, const Rtti *innerType) { - return Argument(std::move(name), &RttiTypes::Map, innerType, nullptr, false); + return Argument(std::move(name), &RttiTypes::Map, innerType, nullptr, + false); } Argument Argument::Map(std::string name, const Rtti *innerType, @@ -276,7 +279,7 @@ bool Arguments::validateArray(Variant::arrayType &arr, Logger &logger) const } bool Arguments::validateMap(Variant::mapType &map, Logger &logger, - bool ignoreUnknown) const + bool ignoreUnknown, bool allowNumericIndices) const { // Abort if no arguments were explicitly given -- everything is valid if (!valid) { @@ -289,28 +292,60 @@ bool Arguments::validateMap(Variant::mapType &map, Logger &logger, const size_t N = arguments.size(); std::vector<bool> set(N); bool ok = true; + std::unordered_map<std::string, std::string> keyReplacements; // Iterate over the map entries and search for the corresponding argument for (auto &e : map) { // Check whether an argument with the name of the current entry exists - auto it = names.find(e.first); + const std::string &key = e.first; + auto it = names.find(key); + ssize_t idx = -1; if (it != names.end()) { // Fetch the corresponding index in the "arguments" array - size_t idx = it->second; + idx = it->second; + } else if (!key.empty() && key[0] == '#' && allowNumericIndices) { + // Read the numeric index + try { + size_t i = stoul(key.substr(1)); + if (i >= 0 && i < arguments.size()) { + idx = i; + keyReplacements.emplace(key, arguments[i].getName()); + } else { + ok = false; + } + } + catch (std::exception ex) { + logger.error( + std::string("Invalid key \"") + key + std::string("\""), + e.second); + ok = false; + } + } + + // If the key could be resolved to an index, validate the argument + if (idx >= 0) { set[idx] = arguments[idx].validate(e.second, logger); ok = ok && set[idx]; } else { if (ignoreUnknown) { logger.note(std::string("Ignoring argument \"") + e.first + - std::string("\""), e.second); + std::string("\""), + e.second); } else { logger.error(std::string("Unknown argument \"") + e.first + - std::string("\""), e.second); + std::string("\""), + e.second); ok = false; } } } + // Execute all the key replacements + for (const auto &replacement : keyReplacements) { + map[replacement.second] = std::move(map[replacement.first]); + map.erase(replacement.first); + } + // Insert all unset arguments for (size_t a = 0; a < N; a++) { if (!set[a]) { diff --git a/src/core/common/Argument.hpp b/src/core/common/Argument.hpp index 679b4a5..39b3bb6 100644 --- a/src/core/common/Argument.hpp +++ b/src/core/common/Argument.hpp @@ -61,13 +61,13 @@ private: /** * Type that should be returned by the Variant rttiType function. */ - Rtti const* type; + Rtti const *type; /** * Describes the inner type of the variant -- e.g. the type of the elements * inside an array. Normally set to RttiTypes::None. */ - Rtti const* innerType; + Rtti const *innerType; /** * Default value. Note that a value of nullptr does not indicate that no @@ -421,7 +421,7 @@ public: * @return the default value that was given in the constructor (may be * nullptr) and nullptr if no default value was given. */ - const Variant& getDefaultValue() const; + const Variant &getDefaultValue() const; /** * Returns true if a default value was set in the constructor. @@ -502,10 +502,13 @@ public: * @param ignoreUnknown if set to true, unknown map entries are ignored * (a note is issued). This behaviour can be usefull if forward * compatibility must be achieved (such as for XML based formats). + * @param allowNumericIndices if set to true, allows numeric indices in the + * input map (such as "#1"). * @return true if the operation was successful, false if an error occured. */ bool validateMap(Variant::mapType &map, Logger &logger, - bool ignoreUnknown = false) const; + bool ignoreUnknown = false, + bool allowNumericIndices = false) const; }; } diff --git a/test/core/common/ArgumentTest.cpp b/test/core/common/ArgumentTest.cpp index 0a2dcfa..ca93f45 100644 --- a/test/core/common/ArgumentTest.cpp +++ b/test/core/common/ArgumentTest.cpp @@ -792,8 +792,8 @@ TEST(Arguments, invalid) Variant::arrayType arr{1}; - ASSERT_TRUE(argsInvalid.validateArray(arr, logger)); // No error message - ASSERT_FALSE(argsValid.validateArray(arr, logger)); // Too many arguments + ASSERT_TRUE(argsInvalid.validateArray(arr, logger)); // No error message + ASSERT_FALSE(argsValid.validateArray(arr, logger)); // Too many arguments } TEST(Arguments, validateArray) @@ -879,6 +879,25 @@ TEST(Arguments, validateMap) {{"a", 2}, {"b", "test"}, {"c", true}, {"d", nullptr}}), map); } + + { + Variant::mapType map{{"#0", 2}, {"#1", "bla"}, {"#2", false}}; + ASSERT_FALSE(args.validateMap(map, logger, false, false)); + ASSERT_EQ(Variant::mapType({{"#0", 2}, + {"#1", "bla"}, + {"#2", false}, + {"a", 0}, + {"b", "test"}, + {"c", true}}), + map); + } + + { + Variant::mapType map{{"#0", 2}, {"#1", "bla"}, {"#2", false}}; + ASSERT_TRUE(args.validateMap(map, logger, false, true)); + ASSERT_EQ(Variant::mapType({{"a", 2}, {"b", "bla"}, {"c", false}}), + map); + } } TEST(Arguments, validateMissing) @@ -897,6 +916,5 @@ TEST(Arguments, validateMissing) ASSERT_EQ(Variant::arrayType({""}), arr); } } - } |