| /* 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); |
| template <typename F> |
| cmJSONObjectHelper& Bind(const cm::string_view& name, 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> |
| template <typename F> |
| cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind( |
| const cm::string_view& name, F func, bool required) |
| { |
| return this->BindPrivate(name, MemberFunction(func), 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(std::move(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); |
| }; |
| } |