summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Source/CMakeLists.txt1
-rw-r--r--Source/cmJSONHelpers.h304
-rw-r--r--Tests/CMakeLib/CMakeLists.txt1
-rw-r--r--Tests/CMakeLib/testJSONHelpers.cxx493
4 files changed, 799 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 310ffeb..1be424a 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -336,6 +336,7 @@ set(SRCS
cmInstallTargetGenerator.cxx
cmInstallDirectoryGenerator.h
cmInstallDirectoryGenerator.cxx
+ cmJSONHelpers.h
cmLDConfigLDConfigTool.cxx
cmLDConfigLDConfigTool.h
cmLDConfigTool.cxx
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h
new file mode 100644
index 0000000..2da2a03
--- /dev/null
+++ b/Source/cmJSONHelpers.h
@@ -0,0 +1,304 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+#include <cm/string_view>
+
+#include <cm3p/json/value.h>
+
+template <typename T, typename E>
+using cmJSONHelper = std::function<E(T& out, const Json::Value* value)>;
+
+template <typename T, typename E>
+class cmJSONObjectHelper
+{
+public:
+ cmJSONObjectHelper(E&& success, E&& fail, bool allowExtra = true);
+
+ template <typename U, typename M, typename F>
+ cmJSONObjectHelper& Bind(const cm::string_view& name, M U::*member, F func,
+ bool required = true);
+ template <typename M, typename F>
+ cmJSONObjectHelper& Bind(const cm::string_view& name, std::nullptr_t, F func,
+ bool required = true);
+
+ E operator()(T& out, const Json::Value* value) const;
+
+private:
+ // Not a true cmJSONHelper, it just happens to match the signature
+ using MemberFunction = std::function<E(T& out, const Json::Value* value)>;
+ struct Member
+ {
+ cm::string_view Name;
+ MemberFunction Function;
+ bool Required;
+ };
+ std::vector<Member> Members;
+ bool AnyRequired = false;
+ E Success;
+ E Fail;
+ bool AllowExtra;
+
+ cmJSONObjectHelper& BindPrivate(const cm::string_view& name,
+ MemberFunction&& func, bool required);
+};
+
+template <typename T, typename E>
+cmJSONObjectHelper<T, E>::cmJSONObjectHelper(E&& success, E&& fail,
+ bool allowExtra)
+ : Success(std::move(success))
+ , Fail(std::move(fail))
+ , AllowExtra(allowExtra)
+{
+}
+
+template <typename T, typename E>
+template <typename U, typename M, typename F>
+cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
+ const cm::string_view& name, M U::*member, F func, bool required)
+{
+ return this->BindPrivate(
+ name,
+ [func, member](T& out, const Json::Value* value) -> E {
+ return func(out.*member, value);
+ },
+ required);
+}
+
+template <typename T, typename E>
+template <typename M, typename F>
+cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind(
+ const cm::string_view& name, std::nullptr_t, F func, bool required)
+{
+ return this->BindPrivate(name,
+ [func](T& /*out*/, const Json::Value* value) -> E {
+ M dummy;
+ return func(dummy, value);
+ },
+ required);
+}
+
+template <typename T, typename E>
+cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::BindPrivate(
+ const cm::string_view& name, MemberFunction&& func, bool required)
+{
+ Member m;
+ m.Name = name;
+ m.Function = std::move(func);
+ m.Required = required;
+ this->Members.push_back(std::move(m));
+ if (required) {
+ this->AnyRequired = true;
+ }
+ return *this;
+}
+
+template <typename T, typename E>
+E cmJSONObjectHelper<T, E>::operator()(T& out, const Json::Value* value) const
+{
+ if (!value && this->AnyRequired) {
+ return this->Fail;
+ }
+ if (value && !value->isObject()) {
+ return this->Fail;
+ }
+ Json::Value::Members extraFields;
+ if (value) {
+ extraFields = value->getMemberNames();
+ }
+
+ for (auto const& m : this->Members) {
+ std::string name(m.Name.data(), m.Name.size());
+ if (value && value->isMember(name)) {
+ E result = m.Function(out, &(*value)[name]);
+ if (result != this->Success) {
+ return result;
+ }
+ extraFields.erase(
+ std::find(extraFields.begin(), extraFields.end(), name));
+ } else if (!m.Required) {
+ E result = m.Function(out, nullptr);
+ if (result != this->Success) {
+ return result;
+ }
+ } else {
+ return this->Fail;
+ }
+ }
+
+ return this->AllowExtra || extraFields.empty() ? this->Success : this->Fail;
+}
+
+template <typename E>
+cmJSONHelper<std::string, E> cmJSONStringHelper(E success, E fail,
+ const std::string& defval = "")
+{
+ return
+ [success, fail, defval](std::string& out, const Json::Value* value) -> E {
+ if (!value) {
+ out = defval;
+ return success;
+ }
+ if (!value->isString()) {
+ return fail;
+ }
+ out = value->asString();
+ return success;
+ };
+}
+
+template <typename E>
+cmJSONHelper<int, E> cmJSONIntHelper(E success, E fail, int defval = 0)
+{
+ return [success, fail, defval](int& out, const Json::Value* value) -> E {
+ if (!value) {
+ out = defval;
+ return success;
+ }
+ if (!value->isInt()) {
+ return fail;
+ }
+ out = value->asInt();
+ return success;
+ };
+}
+
+template <typename E>
+cmJSONHelper<unsigned int, E> cmJSONUIntHelper(E success, E fail,
+ unsigned int defval = 0)
+{
+ return
+ [success, fail, defval](unsigned int& out, const Json::Value* value) -> E {
+ if (!value) {
+ out = defval;
+ return success;
+ }
+ if (!value->isUInt()) {
+ return fail;
+ }
+ out = value->asUInt();
+ return success;
+ };
+}
+
+template <typename E>
+cmJSONHelper<bool, E> cmJSONBoolHelper(E success, E fail, bool defval = false)
+{
+ return [success, fail, defval](bool& out, const Json::Value* value) -> E {
+ if (!value) {
+ out = defval;
+ return success;
+ }
+ if (!value->isBool()) {
+ return fail;
+ }
+ out = value->asBool();
+ return success;
+ };
+}
+
+template <typename T, typename E, typename F, typename Filter>
+cmJSONHelper<std::vector<T>, E> cmJSONVectorFilterHelper(E success, E fail,
+ F func, Filter filter)
+{
+ return [success, fail, func, filter](std::vector<T>& out,
+ const Json::Value* value) -> E {
+ if (!value) {
+ out.clear();
+ return success;
+ }
+ if (!value->isArray()) {
+ return fail;
+ }
+ out.clear();
+ for (auto const& item : *value) {
+ T t;
+ E result = func(t, &item);
+ if (result != success) {
+ return result;
+ }
+ if (!filter(t)) {
+ continue;
+ }
+ out.push_back(t);
+ }
+ return success;
+ };
+}
+
+template <typename T, typename E, typename F>
+cmJSONHelper<std::vector<T>, E> cmJSONVectorHelper(E success, E fail, F func)
+{
+ return cmJSONVectorFilterHelper<T, E, F>(success, fail, func,
+ [](const T&) { return true; });
+}
+
+template <typename T, typename E, typename F, typename Filter>
+cmJSONHelper<std::map<std::string, T>, E> cmJSONMapFilterHelper(E success,
+ E fail, F func,
+ Filter filter)
+{
+ return [success, fail, func, filter](std::map<std::string, T>& out,
+ const Json::Value* value) -> E {
+ if (!value) {
+ out.clear();
+ return success;
+ }
+ if (!value->isObject()) {
+ return fail;
+ }
+ out.clear();
+ for (auto const& key : value->getMemberNames()) {
+ if (!filter(key)) {
+ continue;
+ }
+ T t;
+ E result = func(t, &(*value)[key]);
+ if (result != success) {
+ return result;
+ }
+ out[key] = std::move(t);
+ }
+ return success;
+ };
+}
+
+template <typename T, typename E, typename F>
+cmJSONHelper<std::map<std::string, T>, E> cmJSONMapHelper(E success, E fail,
+ F func)
+{
+ return cmJSONMapFilterHelper<T, E, F>(
+ success, fail, func, [](const std::string&) { return true; });
+}
+
+template <typename T, typename E, typename F>
+cmJSONHelper<cm::optional<T>, E> cmJSONOptionalHelper(E success, F func)
+{
+ return [success, func](cm::optional<T>& out, const Json::Value* value) -> E {
+ if (!value) {
+ out.reset();
+ return success;
+ }
+ out.emplace();
+ return func(*out, value);
+ };
+}
+
+template <typename T, typename E, typename F>
+cmJSONHelper<T, E> cmJSONRequiredHelper(E fail, F func)
+{
+ return [fail, func](T& out, const Json::Value* value) -> E {
+ if (!value) {
+ return fail;
+ }
+ return func(out, value);
+ };
+}
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 0b2c8f6..87925bd 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -13,6 +13,7 @@ set(CMakeLib_TESTS
testCTestResourceGroups.cxx
testGccDepfileReader.cxx
testGeneratedFileStream.cxx
+ testJSONHelpers.cxx
testRST.cxx
testRange.cxx
testOptional.cxx
diff --git a/Tests/CMakeLib/testJSONHelpers.cxx b/Tests/CMakeLib/testJSONHelpers.cxx
new file mode 100644
index 0000000..78eed5b
--- /dev/null
+++ b/Tests/CMakeLib/testJSONHelpers.cxx
@@ -0,0 +1,493 @@
+#include <functional>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONHelpers.h"
+
+#define ASSERT_TRUE(x) \
+ do { \
+ if (!(x)) { \
+ std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \
+ return false; \
+ } \
+ } while (false)
+
+namespace {
+struct ObjectStruct
+{
+ std::string Field1;
+ int Field2;
+};
+
+struct InheritedStruct : public ObjectStruct
+{
+ std::string Field3;
+};
+
+enum class ErrorCode
+{
+ Success,
+ InvalidInt,
+ InvalidBool,
+ InvalidString,
+ InvalidObject,
+ InvalidArray,
+ MissingRequired,
+};
+
+auto const IntHelper =
+ cmJSONIntHelper<ErrorCode>(ErrorCode::Success, ErrorCode::InvalidInt, 1);
+auto const RequiredIntHelper =
+ cmJSONRequiredHelper<int, ErrorCode>(ErrorCode::MissingRequired, IntHelper);
+auto const UIntHelper =
+ cmJSONUIntHelper<ErrorCode>(ErrorCode::Success, ErrorCode::InvalidInt, 1);
+auto const BoolHelper = cmJSONBoolHelper<ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidBool, false);
+auto const StringHelper = cmJSONStringHelper<ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidString, "default");
+auto const RequiredStringHelper = cmJSONRequiredHelper<std::string, ErrorCode>(
+ ErrorCode::MissingRequired, StringHelper);
+auto const StringVectorHelper = cmJSONVectorHelper<std::string, ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidArray, StringHelper);
+auto const StringVectorFilterHelper =
+ cmJSONVectorFilterHelper<std::string, ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidArray, StringHelper,
+ [](const std::string& value) { return value != "ignore"; });
+auto const StringMapHelper = cmJSONMapHelper<std::string, ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidObject, StringHelper);
+auto const StringMapFilterHelper =
+ cmJSONMapFilterHelper<std::string, ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidObject, StringHelper,
+ [](const std::string& key) { return key != "ignore"; });
+auto const OptionalStringHelper =
+ cmJSONOptionalHelper<std::string>(ErrorCode::Success, StringHelper);
+
+bool testInt()
+{
+ Json::Value v(2);
+ int i = 0;
+ ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::Success);
+ ASSERT_TRUE(i == 2);
+
+ i = 0;
+ v = Json::nullValue;
+ ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::InvalidInt);
+
+ i = 0;
+ ASSERT_TRUE(IntHelper(i, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(i == 1);
+
+ return true;
+}
+
+bool testUInt()
+{
+ Json::Value v(2);
+ unsigned int i = 0;
+ ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::Success);
+ ASSERT_TRUE(i == 2);
+
+ i = 0;
+ v = Json::nullValue;
+ ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::InvalidInt);
+
+ i = 0;
+ ASSERT_TRUE(UIntHelper(i, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(i == 1);
+
+ return true;
+}
+
+bool testBool()
+{
+ Json::Value v(true);
+ bool b = false;
+ ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success);
+ ASSERT_TRUE(b);
+
+ b = false;
+ v = false;
+ ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success);
+ ASSERT_TRUE(!b);
+
+ b = false;
+ v = 4;
+ ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::InvalidBool);
+
+ b = true;
+ ASSERT_TRUE(BoolHelper(b, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(!b);
+
+ return true;
+}
+
+bool testString()
+{
+ Json::Value v("str");
+ std::string str = "";
+ ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::Success);
+ ASSERT_TRUE(str == "str");
+
+ str = "";
+ v = Json::nullValue;
+ ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::InvalidString);
+
+ str = "";
+ ASSERT_TRUE(StringHelper(str, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(str == "default");
+
+ return true;
+}
+
+bool testObject()
+{
+ auto const helper = cmJSONObjectHelper<ObjectStruct, ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidObject)
+ .Bind("field1"_s, &ObjectStruct::Field1, StringHelper)
+ .Bind("field2"_s, &ObjectStruct::Field2, IntHelper)
+ .Bind<std::string>("field3"_s, nullptr, StringHelper);
+
+ Json::Value v(Json::objectValue);
+ v["field1"] = "Hello";
+ v["field2"] = 2;
+ v["field3"] = "world!";
+ v["extra"] = "extra";
+
+ ObjectStruct s1;
+ ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(s1.Field1 == "Hello");
+ ASSERT_TRUE(s1.Field2 == 2);
+
+ v["field2"] = "wrong";
+ ObjectStruct s2;
+ ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt);
+
+ v.removeMember("field2");
+ ObjectStruct s3;
+ ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidObject);
+
+ v["field2"] = 2;
+ v["field3"] = 3;
+ ObjectStruct s4;
+ ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidString);
+
+ v.removeMember("field3");
+ ObjectStruct s5;
+ ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidObject);
+
+ v = "Hello";
+ ObjectStruct s6;
+ ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject);
+
+ ObjectStruct s7;
+ ASSERT_TRUE(helper(s7, nullptr) == ErrorCode::InvalidObject);
+
+ return true;
+}
+
+bool testObjectInherited()
+{
+ auto const helper =
+ cmJSONObjectHelper<InheritedStruct, ErrorCode>(ErrorCode::Success,
+ ErrorCode::InvalidObject)
+ .Bind("field1"_s, &InheritedStruct::Field1, StringHelper)
+ .Bind("field2"_s, &InheritedStruct::Field2, IntHelper)
+ .Bind("field3"_s, &InheritedStruct::Field3, StringHelper);
+
+ Json::Value v(Json::objectValue);
+ v["field1"] = "Hello";
+ v["field2"] = 2;
+ v["field3"] = "world!";
+ v["extra"] = "extra";
+
+ InheritedStruct s1;
+ ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(s1.Field1 == "Hello");
+ ASSERT_TRUE(s1.Field2 == 2);
+ ASSERT_TRUE(s1.Field3 == "world!");
+
+ v["field2"] = "wrong";
+ InheritedStruct s2;
+ ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt);
+
+ v.removeMember("field2");
+ InheritedStruct s3;
+ ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidObject);
+
+ v["field2"] = 2;
+ v["field3"] = 3;
+ InheritedStruct s4;
+ ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidString);
+
+ v.removeMember("field3");
+ InheritedStruct s5;
+ ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidObject);
+
+ v = "Hello";
+ InheritedStruct s6;
+ ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject);
+
+ InheritedStruct s7;
+ ASSERT_TRUE(helper(s7, nullptr) == ErrorCode::InvalidObject);
+
+ return true;
+}
+
+bool testObjectNoExtra()
+{
+ auto const helper = cmJSONObjectHelper<ObjectStruct, ErrorCode>(
+ ErrorCode::Success, ErrorCode::InvalidObject, false)
+ .Bind("field1"_s, &ObjectStruct::Field1, StringHelper)
+ .Bind("field2"_s, &ObjectStruct::Field2, IntHelper);
+
+ Json::Value v(Json::objectValue);
+ v["field1"] = "Hello";
+ v["field2"] = 2;
+
+ ObjectStruct s1;
+ ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(s1.Field1 == "Hello");
+ ASSERT_TRUE(s1.Field2 == 2);
+
+ v["extra"] = "world!";
+ ObjectStruct s2;
+ ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidObject);
+
+ return true;
+}
+
+bool testObjectOptional()
+{
+ auto const helper =
+ cmJSONObjectHelper<ObjectStruct, ErrorCode>(ErrorCode::Success,
+ ErrorCode::InvalidObject)
+ .Bind("field1"_s, &ObjectStruct::Field1, StringHelper, false)
+ .Bind("field2"_s, &ObjectStruct::Field2, IntHelper, false)
+ .Bind<std::string>("field3_s", nullptr, StringHelper, false);
+
+ Json::Value v(Json::objectValue);
+ v["field1"] = "Hello";
+ v["field2"] = 2;
+ v["field3"] = "world!";
+ v["extra"] = "extra";
+
+ ObjectStruct s1;
+ ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(s1.Field1 == "Hello");
+ ASSERT_TRUE(s1.Field2 == 2);
+
+ v = Json::objectValue;
+ ObjectStruct s2;
+ ASSERT_TRUE(helper(s2, &v) == ErrorCode::Success);
+ ASSERT_TRUE(s2.Field1 == "default");
+ ASSERT_TRUE(s2.Field2 == 1);
+
+ ObjectStruct s3;
+ ASSERT_TRUE(helper(s3, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(s3.Field1 == "default");
+ ASSERT_TRUE(s3.Field2 == 1);
+
+ return true;
+}
+
+bool testVector()
+{
+ Json::Value v(Json::arrayValue);
+ v.append("Hello");
+ v.append("world!");
+ v.append("ignore");
+
+ std::vector<std::string> l{ "default" };
+ std::vector<std::string> expected{ "Hello", "world!", "ignore" };
+ ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::Success);
+ ASSERT_TRUE(l == expected);
+
+ v[1] = 2;
+ l = { "default" };
+ ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidString);
+
+ v = "Hello";
+ l = { "default" };
+ ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidArray);
+
+ l = { "default" };
+ ASSERT_TRUE(StringVectorHelper(l, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(l.empty());
+
+ return true;
+}
+
+bool testVectorFilter()
+{
+ Json::Value v(Json::arrayValue);
+ v.append("Hello");
+ v.append("world!");
+ v.append("ignore");
+
+ std::vector<std::string> l{ "default" };
+ std::vector<std::string> expected{
+ "Hello",
+ "world!",
+ };
+ ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::Success);
+ ASSERT_TRUE(l == expected);
+
+ v[1] = 2;
+ l = { "default" };
+ ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidString);
+
+ v = "Hello";
+ l = { "default" };
+ ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidArray);
+
+ l = { "default" };
+ ASSERT_TRUE(StringVectorFilterHelper(l, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(l.empty());
+
+ return true;
+}
+
+bool testMap()
+{
+ Json::Value v(Json::objectValue);
+ v["field1"] = "Hello";
+ v["field2"] = "world!";
+ v["ignore"] = "ignore";
+
+ std::map<std::string, std::string> m{ { "key", "default" } };
+ std::map<std::string, std::string> expected{ { "field1", "Hello" },
+ { "field2", "world!" },
+ { "ignore", "ignore" } };
+ ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::Success);
+ ASSERT_TRUE(m == expected);
+
+ v = Json::arrayValue;
+ m = { { "key", "default" } };
+ ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::InvalidObject);
+
+ m = { { "key", "default" } };
+ ASSERT_TRUE(StringMapHelper(m, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(m.empty());
+
+ return true;
+}
+
+bool testMapFilter()
+{
+ Json::Value v(Json::objectValue);
+ v["field1"] = "Hello";
+ v["field2"] = "world!";
+ v["ignore"] = "ignore";
+
+ std::map<std::string, std::string> m{ { "key", "default" } };
+ std::map<std::string, std::string> expected{ { "field1", "Hello" },
+ { "field2", "world!" } };
+ ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::Success);
+ ASSERT_TRUE(m == expected);
+
+ v = Json::arrayValue;
+ m = { { "key", "default" } };
+ ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::InvalidObject);
+
+ m = { { "key", "default" } };
+ ASSERT_TRUE(StringMapFilterHelper(m, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(m.empty());
+
+ return true;
+}
+
+bool testOptional()
+{
+ Json::Value v = "Hello";
+
+ cm::optional<std::string> str{ "default" };
+ ASSERT_TRUE(OptionalStringHelper(str, &v) == ErrorCode::Success);
+ ASSERT_TRUE(str == "Hello");
+
+ str.emplace("default");
+ ASSERT_TRUE(OptionalStringHelper(str, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(str == cm::nullopt);
+
+ return true;
+}
+
+bool testRequired()
+{
+ Json::Value v = "Hello";
+
+ std::string str = "default";
+ int i = 1;
+ ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::Success);
+ ASSERT_TRUE(str == "Hello");
+ ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::InvalidInt);
+
+ v = 2;
+ str = "default";
+ i = 1;
+ ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::InvalidString);
+ ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::Success);
+ ASSERT_TRUE(i == 2);
+
+ str = "default";
+ i = 1;
+ ASSERT_TRUE(RequiredStringHelper(str, nullptr) ==
+ ErrorCode::MissingRequired);
+ ASSERT_TRUE(RequiredIntHelper(i, nullptr) == ErrorCode::MissingRequired);
+
+ return true;
+}
+}
+
+int testJSONHelpers(int /*unused*/, char* /*unused*/ [])
+{
+ if (!testInt()) {
+ return 1;
+ }
+ if (!testUInt()) {
+ return 1;
+ }
+ if (!testBool()) {
+ return 1;
+ }
+ if (!testString()) {
+ return 1;
+ }
+ if (!testObject()) {
+ return 1;
+ }
+ if (!testObjectInherited()) {
+ return 1;
+ }
+ if (!testObjectNoExtra()) {
+ return 1;
+ }
+ if (!testObjectOptional()) {
+ return 1;
+ }
+ if (!testVector()) {
+ return 1;
+ }
+ if (!testVectorFilter()) {
+ return 1;
+ }
+ if (!testMap()) {
+ return 1;
+ }
+ if (!testMapFilter()) {
+ return 1;
+ }
+ if (!testOptional()) {
+ return 1;
+ }
+ if (!testRequired()) {
+ return 1;
+ }
+ return 0;
+}