diff options
author | Kyle Edwards <kyle.edwards@kitware.com> | 2020-09-06 17:24:27 (GMT) |
---|---|---|
committer | Kyle Edwards <kyle.edwards@kitware.com> | 2020-09-09 12:29:02 (GMT) |
commit | 3f3a30e1e06a3461c0e712b4a80b57a5bcb05eab (patch) | |
tree | 3cc5cd65a937e16df7b5c67d18a0342719a23e58 /Source/cmJSONHelpers.h | |
parent | e366977e62edc366e5b7ac38ed6c1c5cf729b428 (diff) | |
download | CMake-3f3a30e1e06a3461c0e712b4a80b57a5bcb05eab.zip CMake-3f3a30e1e06a3461c0e712b4a80b57a5bcb05eab.tar.gz CMake-3f3a30e1e06a3461c0e712b4a80b57a5bcb05eab.tar.bz2 |
JSON: Add helpers
Diffstat (limited to 'Source/cmJSONHelpers.h')
-rw-r--r-- | Source/cmJSONHelpers.h | 304 |
1 files changed, 304 insertions, 0 deletions
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); + }; +} |