/* 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 struct cmJSONHelperBuilder { template class Object { public: Object(E&& success, E&& fail, bool allowExtra = true) : Success(std::move(success)) , Fail(std::move(fail)) , AllowExtra(allowExtra) { } template Object& Bind(const cm::string_view& name, M U::*member, F func, bool required = true) { return this->BindPrivate(name, [func, member](T& out, const Json::Value* value, CallState&&... state) -> E { return func(out.*member, value, std::forward(state)...); }, required); } template Object& Bind(const cm::string_view& name, std::nullptr_t, F func, bool required = true) { return this->BindPrivate(name, [func](T& /*out*/, const Json::Value* value, CallState&&... state) -> E { M dummy; return func(dummy, value, std::forward(state)...); }, required); } template Object& Bind(const cm::string_view& name, F func, bool required = true) { return this->BindPrivate(name, MemberFunction(func), required); } E operator()(T& out, const Json::Value* value, CallState&&... state) 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], std::forward(state)...); 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, std::forward(state)...); if (result != this->Success) { return result; } } else { return this->Fail; } } return this->AllowExtra || extraFields.empty() ? this->Success : this->Fail; } 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; Object& 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; } }; static cmJSONHelper String( E success, E fail, const std::string& defval = "") { return [success, fail, defval](std::string& out, const Json::Value* value, CallState&&... /*state*/) -> E { if (!value) { out = defval; return success; } if (!value->isString()) { return fail; } out = value->asString(); return success; }; } static cmJSONHelper Int(E success, E fail, int defval = 0) { return [success, fail, defval](int& out, const Json::Value* value, CallState&&... /*state*/) -> E { if (!value) { out = defval; return success; } if (!value->isInt()) { return fail; } out = value->asInt(); return success; }; } static cmJSONHelper UInt( E success, E fail, unsigned int defval = 0) { return [success, fail, defval](unsigned int& out, const Json::Value* value, CallState&&... /*state*/) -> E { if (!value) { out = defval; return success; } if (!value->isUInt()) { return fail; } out = value->asUInt(); return success; }; } static cmJSONHelper Bool(E success, E fail, bool defval = false) { return [success, fail, defval](bool& out, const Json::Value* value, CallState&&... /*state*/) -> E { if (!value) { out = defval; return success; } if (!value->isBool()) { return fail; } out = value->asBool(); return success; }; } template static cmJSONHelper, E, CallState...> VectorFilter( E success, E fail, F func, Filter filter) { return [success, fail, func, filter](std::vector& out, const Json::Value* value, CallState&&... state) -> 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, std::forward(state)...); if (result != success) { return result; } if (!filter(t)) { continue; } out.push_back(std::move(t)); } return success; }; } template static cmJSONHelper, E, CallState...> Vector(E success, E fail, F func) { return VectorFilter(success, fail, func, [](const T&) { return true; }); } template static cmJSONHelper, E, CallState...> MapFilter( E success, E fail, F func, Filter filter) { return [success, fail, func, filter](std::map& out, const Json::Value* value, CallState&&... state) -> 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], std::forward(state)...); if (result != success) { return result; } out[key] = std::move(t); } return success; }; } template static cmJSONHelper, E, CallState...> Map(E success, E fail, F func) { return MapFilter(success, fail, func, [](const std::string&) { return true; }); } template static cmJSONHelper, E, CallState...> Optional(E success, F func) { return [success, func](cm::optional& out, const Json::Value* value, CallState&&... state) -> E { if (!value) { out.reset(); return success; } out.emplace(); return func(*out, value, std::forward(state)...); }; } template static cmJSONHelper Required(E fail, F func) { return [fail, func](T& out, const Json::Value* value, CallState&&... state) -> E { if (!value) { return fail; } return func(out, value, std::forward(state)...); }; } };