summaryrefslogtreecommitdiffstats
path: root/Source/cmJSONHelpers.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmJSONHelpers.h')
-rw-r--r--Source/cmJSONHelpers.h352
1 files changed, 258 insertions, 94 deletions
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h
index f7151b5..94641de 100644
--- a/Source/cmJSONHelpers.h
+++ b/Source/cmJSONHelpers.h
@@ -2,9 +2,12 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
+#include "cmConfigure.h" // IWYU pragma: keep
+
#include <algorithm>
#include <cstddef>
#include <functional>
+#include <iostream>
#include <map>
#include <string>
#include <vector>
@@ -14,20 +17,129 @@
#include <cm3p/json/value.h>
-template <typename T, typename E, typename... CallState>
+#include "cmJSONState.h"
+
+template <typename T>
using cmJSONHelper =
- std::function<E(T& out, const Json::Value* value, CallState&&... state)>;
+ std::function<bool(T& out, const Json::Value* value, cmJSONState* state)>;
+
+using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
+
+namespace JsonErrors {
+enum ObjectError
+{
+ RequiredMissing,
+ InvalidObject,
+ ExtraField,
+ MissingRequired
+};
+using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
+using ObjectErrorGenerator =
+ std::function<ErrorGenerator(ObjectError, const Json::Value::Members&)>;
+const auto EXPECTED_TYPE = [](const std::string& type) {
+ return [type](const Json::Value* value, cmJSONState* state) -> void {
+#if !defined(CMAKE_BOOTSTRAP)
+ if (state->key().empty()) {
+ state->AddErrorAtValue(cmStrCat("Expected ", type), value);
+ return;
+ }
+ std::string errMsg = cmStrCat("\"", state->key(), "\" expected ", type);
+ if (value && value->isConvertibleTo(Json::ValueType::stringValue)) {
+ errMsg = cmStrCat(errMsg, ", got: ", value->asString());
+ }
+ state->AddErrorAtValue(errMsg, value);
+#endif
+ };
+};
+const auto INVALID_STRING = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("a string")(value, state);
+};
+const auto INVALID_BOOL = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("a bool")(value, state);
+};
+const auto INVALID_INT = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("an integer")(value, state);
+};
+const auto INVALID_UINT = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("an unsigned integer")(value, state);
+};
+const auto INVALID_NAMED_OBJECT =
+ [](const std::function<std::string(const Json::Value*, cmJSONState*)>&
+ nameGenerator) -> ObjectErrorGenerator {
+ return [nameGenerator](
+ ObjectError errorType,
+ const Json::Value::Members& extraFields) -> ErrorGenerator {
+ return [nameGenerator, errorType, extraFields](
+ const Json::Value* value, cmJSONState* state) -> void {
+#if !defined(CMAKE_BOOTSTRAP)
+ std::string name = nameGenerator(value, state);
+ switch (errorType) {
+ case ObjectError::RequiredMissing:
+ state->AddErrorAtValue(cmStrCat("Invalid Required ", name), value);
+ break;
+ case ObjectError::InvalidObject:
+ state->AddErrorAtValue(cmStrCat("Invalid ", name), value);
+ break;
+ case ObjectError::ExtraField: {
+ for (auto const& member : extraFields) {
+ if (value) {
+ state->AddErrorAtValue(
+ cmStrCat("Invalid extra field \"", member, "\" in ", name),
+ &(*value)[member]);
+ } else {
+ state->AddError(
+ cmStrCat("Invalid extra field \"", member, "\" in ", name));
+ }
+ }
+ } break;
+ case ObjectError::MissingRequired:
+ state->AddErrorAtValue(cmStrCat("Missing required field \"",
+ state->key(), "\" in ", name),
+ value);
+ break;
+ }
+#endif
+ };
+ };
+};
+const auto INVALID_OBJECT =
+ [](ObjectError errorType,
+ const Json::Value::Members& extraFields) -> ErrorGenerator {
+ return INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState*) -> std::string { return "Object"; })(
+ errorType, extraFields);
+};
+const auto INVALID_NAMED_OBJECT_KEY =
+ [](ObjectError errorType,
+ const Json::Value::Members& extraFields) -> ErrorGenerator {
+ return INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState* state) -> std::string {
+ for (auto it = state->parseStack.rbegin();
+ it != state->parseStack.rend(); ++it) {
+ if (it->first.rfind("$vector_item_", 0) == 0) {
+ continue;
+ }
+ return cmStrCat("\"", it->first, "\"");
+ }
+ return "root";
+ })(errorType, extraFields);
+};
+}
-template <typename E, typename... CallState>
struct cmJSONHelperBuilder
{
+
template <typename T>
class Object
{
public:
- Object(E&& success, E&& fail, bool allowExtra = true)
- : Success(std::move(success))
- , Fail(std::move(fail))
+ Object(JsonErrors::ObjectErrorGenerator error = JsonErrors::INVALID_OBJECT,
+ bool allowExtra = true)
+ : Error(std::move(error))
, AllowExtra(allowExtra)
{
}
@@ -38,8 +150,8 @@ struct cmJSONHelperBuilder
{
return this->BindPrivate(
name,
- [func, member](T& out, const Json::Value* value, CallState&&... state)
- -> E { return func(out.*member, value, std::forward(state)...); },
+ [func, member](T& out, const Json::Value* value, cmJSONState* state)
+ -> bool { return func(out.*member, value, state); },
required);
}
template <typename M, typename F>
@@ -49,9 +161,9 @@ struct cmJSONHelperBuilder
return this->BindPrivate(
name,
[func](T& /*out*/, const Json::Value* value,
- CallState&&... state) -> E {
+ cmJSONState* state) -> bool {
M dummy;
- return func(dummy, value, std::forward(state)...);
+ return func(dummy, value, state);
},
required);
}
@@ -61,46 +173,56 @@ struct cmJSONHelperBuilder
return this->BindPrivate(name, MemberFunction(func), required);
}
- E operator()(T& out, const Json::Value* value, CallState&&... state) const
+ bool operator()(T& out, const Json::Value* value, cmJSONState* state) const
{
+ Json::Value::Members extraFields;
+ bool success = true;
if (!value && this->AnyRequired) {
- return this->Fail;
+ Error(JsonErrors::ObjectError::RequiredMissing, extraFields)(value,
+ state);
+ return false;
}
if (value && !value->isObject()) {
- return this->Fail;
+ Error(JsonErrors::ObjectError::InvalidObject, extraFields)(value,
+ state);
+ return false;
}
- Json::Value::Members extraFields;
if (value) {
extraFields = value->getMemberNames();
}
for (auto const& m : this->Members) {
std::string name(m.Name.data(), m.Name.size());
+ state->push_stack(name, value);
if (value && value->isMember(name)) {
- E result = m.Function(out, &(*value)[name], std::forward(state)...);
- if (result != this->Success) {
- return result;
+ if (!m.Function(out, &(*value)[name], state)) {
+ success = false;
}
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;
+ if (!m.Function(out, nullptr, state)) {
+ success = false;
}
} else {
- return this->Fail;
+ Error(JsonErrors::ObjectError::MissingRequired, extraFields)(value,
+ state);
+ success = false;
}
+ state->pop_stack();
}
- return this->AllowExtra || extraFields.empty() ? this->Success
- : this->Fail;
+ if (!this->AllowExtra && !extraFields.empty()) {
+ Error(JsonErrors::ObjectError::ExtraField, extraFields)(value, state);
+ success = false;
+ }
+ return success;
}
private:
// Not a true cmJSONHelper, it just happens to match the signature
- using MemberFunction =
- std::function<E(T& out, const Json::Value* value, CallState&&... state)>;
+ using MemberFunction = std::function<bool(T& out, const Json::Value* value,
+ cmJSONState* state)>;
struct Member
{
cm::string_view Name;
@@ -109,8 +231,7 @@ struct cmJSONHelperBuilder
};
std::vector<Member> Members;
bool AnyRequired = false;
- E Success;
- E Fail;
+ JsonErrors::ObjectErrorGenerator Error;
bool AllowExtra;
Object& BindPrivate(const cm::string_view& name, MemberFunction&& func,
@@ -127,175 +248,218 @@ struct cmJSONHelperBuilder
return *this;
}
};
- static cmJSONHelper<std::string, E, CallState...> String(
- E success, E fail, const std::string& defval = "")
+
+ static cmJSONHelper<std::string> String(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_STRING,
+ const std::string& defval = "")
{
- return [success, fail, defval](std::string& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](std::string& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isString()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asString();
- return success;
+ return true;
};
- }
+ };
+
+ static cmJSONHelper<std::string> String(const std::string& defval)
+ {
+ return String(JsonErrors::INVALID_STRING, defval);
+ };
- static cmJSONHelper<int, E, CallState...> Int(E success, E fail,
- int defval = 0)
+ static cmJSONHelper<int> Int(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_INT,
+ int defval = 0)
{
- return [success, fail, defval](int& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](int& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isInt()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asInt();
- return success;
+ return true;
};
}
- static cmJSONHelper<unsigned int, E, CallState...> UInt(
- E success, E fail, unsigned int defval = 0)
+ static cmJSONHelper<int> Int(int defval)
+ {
+ return Int(JsonErrors::INVALID_INT, defval);
+ };
+
+ static cmJSONHelper<unsigned int> UInt(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_UINT,
+ unsigned int defval = 0)
{
- return [success, fail, defval](unsigned int& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](unsigned int& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isUInt()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asUInt();
- return success;
+ return true;
};
}
- static cmJSONHelper<bool, E, CallState...> Bool(E success, E fail,
- bool defval = false)
+ static cmJSONHelper<unsigned int> UInt(unsigned int defval)
+ {
+ return UInt(JsonErrors::INVALID_UINT, defval);
+ }
+
+ static cmJSONHelper<bool> Bool(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_BOOL,
+ bool defval = false)
{
- return [success, fail, defval](bool& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](bool& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isBool()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asBool();
- return success;
+ return true;
};
}
+ static cmJSONHelper<bool> Bool(bool defval)
+ {
+ return Bool(JsonErrors::INVALID_BOOL, defval);
+ }
+
template <typename T, typename F, typename Filter>
- static cmJSONHelper<std::vector<T>, E, CallState...> VectorFilter(
- E success, E fail, F func, Filter filter)
+ static cmJSONHelper<std::vector<T>> VectorFilter(
+ const JsonErrors::ErrorGenerator& error, F func, Filter filter)
{
- return [success, fail, func, filter](std::vector<T>& out,
- const Json::Value* value,
- CallState&&... state) -> E {
+ return [error, func, filter](std::vector<T>& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
+ bool success = true;
if (!value) {
out.clear();
- return success;
+ return true;
}
if (!value->isArray()) {
- return fail;
+ error(value, state);
+ return false;
}
out.clear();
+ int index = 0;
for (auto const& item : *value) {
+ state->push_stack(cmStrCat("$vector_item_", index++), &item);
T t;
- E result = func(t, &item, std::forward(state)...);
- if (result != success) {
- return result;
+ if (!func(t, &item, state)) {
+ success = false;
}
if (!filter(t)) {
+ state->pop_stack();
continue;
}
out.push_back(std::move(t));
+ state->pop_stack();
}
return success;
};
}
template <typename T, typename F>
- static cmJSONHelper<std::vector<T>, E, CallState...> Vector(E success,
- E fail, F func)
+ static cmJSONHelper<std::vector<T>> Vector(JsonErrors::ErrorGenerator error,
+ F func)
{
- return VectorFilter<T, F>(success, fail, func,
+ return VectorFilter<T, F>(std::move(error), func,
[](const T&) { return true; });
}
template <typename T, typename F, typename Filter>
- static cmJSONHelper<std::map<std::string, T>, E, CallState...> MapFilter(
- E success, E fail, F func, Filter filter)
+ static cmJSONHelper<std::map<std::string, T>> MapFilter(
+ const JsonErrors::ErrorGenerator& error, F func, Filter filter)
{
- return [success, fail, func, filter](std::map<std::string, T>& out,
- const Json::Value* value,
- CallState&&... state) -> E {
+ return [error, func, filter](std::map<std::string, T>& out,
+ const Json::Value* value,
+ cmJSONState* state) -> bool {
+ bool success = true;
if (!value) {
out.clear();
- return success;
+ return true;
}
if (!value->isObject()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out.clear();
for (auto const& key : value->getMemberNames()) {
+ state->push_stack(cmStrCat(key, ""), &(*value)[key]);
if (!filter(key)) {
+ state->pop_stack();
continue;
}
T t;
- E result = func(t, &(*value)[key], std::forward(state)...);
- if (result != success) {
- return result;
+ if (!func(t, &(*value)[key], state)) {
+ success = false;
}
out[key] = std::move(t);
+ state->pop_stack();
}
return success;
};
}
template <typename T, typename F>
- static cmJSONHelper<std::map<std::string, T>, E, CallState...> Map(E success,
- E fail,
- F func)
+ static cmJSONHelper<std::map<std::string, T>> Map(
+ const JsonErrors::ErrorGenerator& error, F func)
{
- return MapFilter<T, F>(success, fail, func,
+ return MapFilter<T, F>(error, func,
[](const std::string&) { return true; });
}
template <typename T, typename F>
- static cmJSONHelper<cm::optional<T>, E, CallState...> Optional(E success,
- F func)
+ static cmJSONHelper<cm::optional<T>> Optional(F func)
{
- return [success, func](cm::optional<T>& out, const Json::Value* value,
- CallState&&... state) -> E {
+ return [func](cm::optional<T>& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out.reset();
- return success;
+ return true;
}
out.emplace();
- return func(*out, value, std::forward(state)...);
+ return func(*out, value, state);
};
}
template <typename T, typename F>
- static cmJSONHelper<T, E, CallState...> Required(E fail, F func)
+ static cmJSONHelper<T> Required(const JsonErrors::ErrorGenerator& error,
+ F func)
{
- return [fail, func](T& out, const Json::Value* value,
- CallState&&... state) -> E {
+ return [error, func](T& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
- return func(out, value, std::forward(state)...);
+ return func(out, value, state);
};
}
};