geode/loader/include/Geode/utils/JsonValidation.hpp

308 lines
9.7 KiB
C++
Raw Normal View History

#pragma once
#include "../external/json/json.hpp"
2022-12-04 11:39:40 -05:00
#include "../loader/Log.hpp"
2022-10-30 14:59:20 -04:00
#include <set>
2022-10-30 14:59:20 -04:00
#include <variant>
namespace geode {
2022-10-30 14:59:20 -04:00
template <class Json>
struct JsonChecker;
template <typename T, typename = void>
struct is_iterable : std::false_type {};
template <typename T>
2022-10-30 14:59:20 -04:00
struct is_iterable<
2022-12-04 11:39:40 -05:00
T, std::void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>> :
2022-10-30 14:59:20 -04:00
std::true_type {};
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
namespace {
using value_t = nlohmann::detail::value_t;
2022-10-30 14:59:20 -04:00
constexpr char const* jsonValueTypeToString(value_t type) {
switch (type) {
default:
2022-10-30 14:59:20 -04:00
case value_t::null: return "null";
case value_t::object: return "object";
case value_t::array: return "array";
case value_t::string: return "string";
case value_t::boolean: return "boolean";
case value_t::binary: return "binary";
case value_t::discarded: return "discarded";
case value_t::number_integer: return "integer";
case value_t::number_unsigned: return "integer";
2022-10-30 14:59:20 -04:00
case value_t::number_float: return "number";
}
}
2022-10-30 14:59:20 -04:00
template <class T>
constexpr value_t getJsonType() {
if constexpr (std::is_same_v<T, bool>) {
return value_t::boolean;
}
else if constexpr (std::is_floating_point_v<T>) {
return value_t::number_float;
}
else if constexpr (std::is_unsigned_v<T>) {
return value_t::number_unsigned;
}
else if constexpr (std::is_integral_v<T>) {
return value_t::number_integer;
}
2022-10-30 14:59:20 -04:00
else if constexpr (std::is_constructible_v<T, std::string>) {
return value_t::string;
}
else if constexpr (is_iterable_v<T>) {
return value_t::array;
}
return value_t::null;
}
bool jsonConvertibleTo(value_t value, value_t to) {
2022-10-30 14:59:20 -04:00
// if we don't know the type we're passing into,
2022-09-21 07:50:23 -04:00
// everything's valid
if (to == value_t::null) return true;
2022-10-30 14:59:20 -04:00
if (value == value_t::number_float || value == value_t::number_integer ||
value == value_t::number_unsigned) {
return to == value_t::number_float || to == value_t::number_integer ||
to == value_t::number_unsigned;
}
return value == to;
}
}
2022-10-30 14:59:20 -04:00
template <class T>
2022-12-04 11:39:40 -05:00
using JsonValueValidator = std::function<bool(T const&)>;
2022-10-30 14:59:20 -04:00
template <class Json>
struct JsonMaybeObject;
2022-10-30 14:59:20 -04:00
template <class Json>
2022-09-24 11:46:47 -04:00
struct JsonMaybeValue;
2022-10-30 14:59:20 -04:00
template <class Json>
2022-09-24 11:46:47 -04:00
struct JsonMaybeSomething {
protected:
2022-09-24 11:46:47 -04:00
JsonChecker<Json>& m_checker;
Json& m_json;
std::string m_hierarchy;
bool m_hasValue;
2022-09-24 11:46:47 -04:00
friend struct JsonMaybeObject<Json>;
friend struct JsonMaybeValue<Json>;
2022-10-23 09:22:27 -04:00
GEODE_DLL void setError(std::string const& error);
2022-10-30 14:59:20 -04:00
public:
2022-10-23 09:22:27 -04:00
GEODE_DLL Json& json();
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeSomething(
2022-10-30 14:59:20 -04:00
JsonChecker<Json>& checker, Json& json, std::string const& hierarchy, bool hasValue
2022-10-23 09:22:27 -04:00
);
2022-10-23 09:22:27 -04:00
GEODE_DLL bool isError() const;
GEODE_DLL std::string getError() const;
2022-10-23 09:22:27 -04:00
GEODE_DLL operator bool() const;
};
2022-10-30 14:59:20 -04:00
template <class Json>
2022-09-24 11:46:47 -04:00
struct JsonMaybeValue : public JsonMaybeSomething<Json> {
bool m_inferType = true;
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue(
2022-10-30 14:59:20 -04:00
JsonChecker<Json>& checker, Json& json, std::string const& hierarchy, bool hasValue
2022-10-23 09:22:27 -04:00
);
2022-09-24 11:46:47 -04:00
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeSomething<Json>& self();
2022-10-30 14:59:20 -04:00
template <nlohmann::detail::value_t T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& as() {
if (this->isError()) return *this;
2022-09-24 11:46:47 -04:00
if (!jsonConvertibleTo(self().m_json.type(), T)) {
this->setError(
2022-10-30 14:59:20 -04:00
self().m_hierarchy + ": Invalid type \"" + self().m_json.type_name() +
"\", expected \"" + jsonValueTypeToString(T) + "\""
);
}
m_inferType = false;
return *this;
}
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue<Json>& array();
2022-10-30 14:59:20 -04:00
template <nlohmann::detail::value_t... T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& asOneOf() {
if (this->isError()) return *this;
2022-09-24 11:46:47 -04:00
bool isOneOf = (... || jsonConvertibleTo(self().m_json.type(), T));
if (!isOneOf) {
this->setError(
2022-10-30 14:59:20 -04:00
self().m_hierarchy + ": Invalid type \"" + self().m_json.type_name() +
"\", expected one of \"" + (jsonValueTypeToString(T), ...) + "\""
);
}
m_inferType = false;
return *this;
}
2022-10-30 14:59:20 -04:00
template <nlohmann::detail::value_t T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& is() {
if (this->isError()) return *this;
2022-09-24 11:46:47 -04:00
self().m_hasValue = jsonConvertibleTo(self().m_json.type(), T);
m_inferType = false;
return *this;
}
2022-10-30 14:59:20 -04:00
template <class T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& validate(JsonValueValidator<T> validator) {
if (this->isError()) return *this;
try {
2022-09-28 09:21:05 -04:00
if (!validator(self().m_json.template get<T>())) {
2022-09-24 11:46:47 -04:00
this->setError(self().m_hierarchy + ": Invalid value format");
}
2022-10-30 14:59:20 -04:00
}
catch (...) {
this->setError(
2022-09-24 11:46:47 -04:00
self().m_hierarchy + ": Invalid type \"" +
std::string(self().m_json.type_name()) + "\""
);
}
return *this;
}
2022-12-04 11:39:40 -05:00
template <class T>
JsonMaybeValue<Json>& validate(bool (*validator)(T const&)) {
return this->validate(std::function(validator));
}
2022-10-30 14:59:20 -04:00
template <class T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& inferType() {
if (this->isError() || !m_inferType) return *this;
return this->as<getJsonType<T>()>();
}
2022-10-30 14:59:20 -04:00
template <class T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& intoRaw(T& target) {
if (this->isError()) return *this;
2022-09-24 11:46:47 -04:00
target = self().m_json;
return *this;
}
2022-10-30 14:59:20 -04:00
template <class T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& into(T& target) {
return this->intoAs<T, T>(target);
}
2022-10-30 14:59:20 -04:00
template <class T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& into(std::optional<T>& target) {
return this->intoAs<T, std::optional<T>>(target);
}
2022-10-30 14:59:20 -04:00
template <class A, class T>
2022-10-23 09:22:27 -04:00
JsonMaybeValue<Json>& intoAs(T& target) {
this->inferType<A>();
if (this->isError()) return *this;
try {
2022-09-28 09:21:05 -04:00
target = self().m_json.template get<A>();
2022-10-30 14:59:20 -04:00
}
catch (...) {
this->setError(
2022-10-30 14:59:20 -04:00
self().m_hierarchy + ": Invalid type \"" +
2022-09-24 11:46:47 -04:00
std::string(self().m_json.type_name()) + "\""
);
}
return *this;
}
2022-10-30 14:59:20 -04:00
template <class T>
T get() {
this->inferType<T>();
if (this->isError()) return T();
try {
2022-09-28 09:21:05 -04:00
return self().m_json.template get<T>();
2022-10-30 14:59:20 -04:00
}
catch (...) {
this->setError(
2022-10-30 14:59:20 -04:00
self().m_hierarchy + ": Invalid type to get \"" +
2022-09-24 11:46:47 -04:00
std::string(self().m_json.type_name()) + "\""
);
}
return T();
}
GEODE_DLL JsonMaybeObject<Json> obj();
2022-10-30 14:59:20 -04:00
template <class T>
struct Iterator {
std::vector<T> m_values;
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
iterator begin() {
return m_values.begin();
}
2022-10-30 14:59:20 -04:00
iterator end() {
return m_values.end();
}
const_iterator begin() const {
return m_values.begin();
}
2022-10-30 14:59:20 -04:00
const_iterator end() const {
return m_values.end();
}
};
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue<Json> at(size_t i);
2022-10-23 09:22:27 -04:00
GEODE_DLL Iterator<JsonMaybeValue<Json>> iterate();
2022-10-23 09:22:27 -04:00
GEODE_DLL Iterator<std::pair<std::string, JsonMaybeValue<Json>>> items();
};
2022-10-30 14:59:20 -04:00
template <class Json>
2022-09-24 11:46:47 -04:00
struct JsonMaybeObject : JsonMaybeSomething<Json> {
std::set<std::string> m_knownKeys;
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeObject(
2022-10-30 14:59:20 -04:00
JsonChecker<Json>& checker, Json& json, std::string const& hierarchy, bool hasValue
2022-10-23 09:22:27 -04:00
);
2022-09-24 11:46:47 -04:00
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeSomething<Json>& self();
2022-10-23 09:22:27 -04:00
GEODE_DLL void addKnownKey(std::string const& key);
2022-10-23 09:22:27 -04:00
GEODE_DLL Json& json();
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue<Json> emptyValue();
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue<Json> has(std::string const& key);
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue<Json> needs(std::string const& key);
2022-10-23 09:22:27 -04:00
GEODE_DLL void checkUnknownKeys();
};
2022-10-30 14:59:20 -04:00
template <class Json = nlohmann::json>
2022-09-24 11:46:47 -04:00
struct JsonChecker {
std::variant<std::monostate, std::string> m_result;
2022-09-24 11:46:47 -04:00
Json& m_json;
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonChecker(Json& json);
2022-10-23 09:22:27 -04:00
GEODE_DLL bool isError() const;
2022-10-23 09:22:27 -04:00
GEODE_DLL std::string getError() const;
2022-10-23 09:22:27 -04:00
GEODE_DLL JsonMaybeValue<Json> root(std::string const& hierarchy);
};
2022-10-30 14:59:20 -04:00
}