/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once #include #include #include #include #include #include #include #include #include template using cmJSONHelper = std::function; template class cmJSONObjectHelper { public: cmJSONObjectHelper(E&& success, E&& fail, bool allowExtra = true); template cmJSONObjectHelper& Bind(const cm::string_view& name, M U::*member, F func, bool required = true); template 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; struct Member { cm::string_view Name; MemberFunction Function; bool Required; }; std::vector Members; bool AnyRequired = false; E Success; E Fail; bool AllowExtra; cmJSONObjectHelper& BindPrivate(const cm::string_view& name, MemberFunction&& func, bool required); }; template cmJSONObjectHelper::cmJSONObjectHelper(E&& success, E&& fail, bool allowExtra) : Success(std::move(success)) , Fail(std::move(fail)) , AllowExtra(allowExtra) { } template template cmJSONObjectHelper& cmJSONObjectHelper::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 template cmJSONObjectHelper& cmJSONObjectHelper::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 cmJSONObjectHelper& cmJSONObjectHelper::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 E cmJSONObjectHelper::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 cmJSONHelper 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 cmJSONHelper 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 cmJSONHelper 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 cmJSONHelper 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 cmJSONHelper, E> cmJSONVectorFilterHelper(E success, E fail, F func, Filter filter) { return [success, fail, func, filter](std::vector& 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 cmJSONHelper, E> cmJSONVectorHelper(E success, E fail, F func) { return cmJSONVectorFilterHelper(success, fail, func, [](const T&) { return true; }); } template cmJSONHelper, E> cmJSONMapFilterHelper(E success, E fail, F func, Filter filter) { return [success, fail, func, filter](std::map& 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 cmJSONHelper, E> cmJSONMapHelper(E success, E fail, F func) { return cmJSONMapFilterHelper( success, fail, func, [](const std::string&) { return true; }); } template cmJSONHelper, E> cmJSONOptionalHelper(E success, F func) { return [success, func](cm::optional& out, const Json::Value* value) -> E { if (!value) { out.reset(); return success; } out.emplace(); return func(*out, value); }; } template cmJSONHelper cmJSONRequiredHelper(E fail, F func) { return [fail, func](T& out, const Json::Value* value) -> E { if (!value) { return fail; } return func(out, value); }; }