You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
8.5 KiB
316 lines
8.5 KiB
/* 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);
|
|
};
|
|
}
|