268 lines
12 KiB
C++
268 lines
12 KiB
C++
#pragma once
|
|
#include <concepts>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
namespace adminive {
|
|
enum class Write_Mode {
|
|
internal,
|
|
create,
|
|
update
|
|
};
|
|
struct Write_Context {
|
|
Write_Mode mode{Write_Mode::internal};
|
|
bool complete{true};
|
|
};
|
|
template <class T>
|
|
struct Reflection_Adapter;
|
|
template <class T>
|
|
struct Object_Adapter {
|
|
using model_type = std::remove_cvref_t<T>;
|
|
static model_type snapshot(const model_type& value) requires std::copy_constructible<model_type> {
|
|
return value;
|
|
}
|
|
static model_type create() requires std::default_initializable<model_type> {
|
|
return {};
|
|
}
|
|
static void commit(model_type& target, model_type value) requires std::assignable_from<model_type&, model_type> {
|
|
target = std::move(value);
|
|
}
|
|
};
|
|
template <class T>
|
|
using Object_Model_Type = typename Object_Adapter<std::remove_cvref_t<T>>::model_type;
|
|
template <class T>
|
|
concept Object_Adapter_With_Snapshot = requires(const std::remove_cvref_t<T>& value) {
|
|
{ Object_Adapter<std::remove_cvref_t<T>>::snapshot(value) } -> std::same_as<Object_Model_Type<T>>;
|
|
};
|
|
template <class T>
|
|
concept Object_Adapter_With_Create = requires {
|
|
{ Object_Adapter<std::remove_cvref_t<T>>::create() } -> std::same_as<Object_Model_Type<T>>;
|
|
};
|
|
template <class T>
|
|
concept Object_Adapter_With_Commit = requires(std::remove_cvref_t<T>& target, Object_Model_Type<T> value) {
|
|
Object_Adapter<std::remove_cvref_t<T>>::commit(target, std::move(value));
|
|
};
|
|
template <class Json>
|
|
struct Json_Adapter;
|
|
template <class Json, class T>
|
|
concept Json_Scalar_Makeable = requires(T value) {
|
|
{ Json_Adapter<Json>::make(std::move(value)) } -> std::same_as<Json>;
|
|
};
|
|
template <class Json, class T>
|
|
concept Json_Scalar_Gettable = requires(const Json& value) {
|
|
{ Json_Adapter<Json>::template get<T>(value) } -> std::same_as<T>;
|
|
};
|
|
template <class Json>
|
|
concept Json_Type = requires(Json value, const Json const_value, std::string_view text, std::size_t index) {
|
|
{ Json_Adapter<Json>::object() } -> std::same_as<Json>;
|
|
{ Json_Adapter<Json>::array() } -> std::same_as<Json>;
|
|
{ Json_Adapter<Json>::null() } -> std::same_as<Json>;
|
|
{ Json_Adapter<Json>::parse(text) } -> std::same_as<Json>;
|
|
{ Json_Adapter<Json>::dump(const_value, 2) } -> std::same_as<std::string>;
|
|
{ Json_Adapter<Json>::is_null(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_object(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_array(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_string(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_signed_integer(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_unsigned_integer(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_floating_point(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_number(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::is_boolean(const_value) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::signed_integer(const_value) } -> std::same_as<std::int64_t>;
|
|
{ Json_Adapter<Json>::unsigned_integer(const_value) } -> std::same_as<std::uint64_t>;
|
|
{ Json_Adapter<Json>::number(const_value) } -> std::convertible_to<long double>;
|
|
{ Json_Adapter<Json>::contains(const_value, text) } -> std::same_as<bool>;
|
|
{ Json_Adapter<Json>::size(const_value) } -> std::convertible_to<std::size_t>;
|
|
{ Json_Adapter<Json>::keys(const_value) } -> std::same_as<std::vector<std::string>>;
|
|
{ Json_Adapter<Json>::at(value, text) } -> std::same_as<Json&>;
|
|
{ Json_Adapter<Json>::at(const_value, text) } -> std::same_as<const Json&>;
|
|
{ Json_Adapter<Json>::at(value, index) } -> std::same_as<Json&>;
|
|
{ Json_Adapter<Json>::at(const_value, index) } -> std::same_as<const Json&>;
|
|
Json_Adapter<Json>::set(value, text, Json{});
|
|
Json_Adapter<Json>::append(value, Json{});
|
|
Json_Adapter<Json>::erase(value, text);
|
|
} && Json_Scalar_Makeable<Json, bool> && Json_Scalar_Makeable<Json, std::int64_t> && Json_Scalar_Makeable<Json, std::uint64_t> && Json_Scalar_Makeable<Json, double> && Json_Scalar_Makeable<Json, std::string> && Json_Scalar_Gettable<Json, bool> && Json_Scalar_Gettable<Json, std::int64_t> && Json_Scalar_Gettable<Json, std::uint64_t> && Json_Scalar_Gettable<Json, double> && Json_Scalar_Gettable<Json, std::string>;
|
|
template <Json_Type Json>
|
|
Json json_object() {
|
|
return Json_Adapter<Json>::object();
|
|
}
|
|
template <Json_Type Json>
|
|
Json json_array() {
|
|
return Json_Adapter<Json>::array();
|
|
}
|
|
template <Json_Type Json>
|
|
Json json_null() {
|
|
return Json_Adapter<Json>::null();
|
|
}
|
|
template <Json_Type Json, class T>
|
|
Json json_scalar(T&& value) {
|
|
using Value = std::remove_cvref_t<T>;
|
|
if constexpr(std::is_array_v<Value> && std::same_as<std::remove_cv_t<std::remove_extent_t<Value>>, char>) {
|
|
return Json_Adapter<Json>::make(std::string(value));
|
|
} else if constexpr(std::same_as<Value, const char*> || std::same_as<Value, char*> || std::same_as<Value, std::string_view>) {
|
|
return Json_Adapter<Json>::make(std::string(value));
|
|
} else if constexpr(std::signed_integral<Value> && !std::same_as<Value, bool>) {
|
|
return Json_Adapter<Json>::make(static_cast<std::int64_t>(value));
|
|
} else if constexpr(std::unsigned_integral<Value> && !std::same_as<Value, bool>) {
|
|
return Json_Adapter<Json>::make(static_cast<std::uint64_t>(value));
|
|
} else if constexpr(std::floating_point<Value>) {
|
|
return Json_Adapter<Json>::make(static_cast<double>(value));
|
|
} else {
|
|
static_assert(Json_Scalar_Makeable<Json, Value>, "JSON adapter cannot construct this scalar type");
|
|
return Json_Adapter<Json>::make(std::forward<T>(value));
|
|
}
|
|
}
|
|
template <Json_Type Json, class T>
|
|
void json_set(Json& object, std::string_view name, T&& value) {
|
|
if constexpr(std::same_as<std::remove_cvref_t<T>, Json>) {
|
|
Json_Adapter<Json>::set(object, name, std::forward<T>(value));
|
|
} else {
|
|
Json_Adapter<Json>::set(object, name, json_scalar<Json>(std::forward<T>(value)));
|
|
}
|
|
}
|
|
template <Json_Type Json, class T>
|
|
void json_append(Json& array, T&& value) {
|
|
if constexpr(std::same_as<std::remove_cvref_t<T>, Json>) {
|
|
Json_Adapter<Json>::append(array, std::forward<T>(value));
|
|
} else {
|
|
Json_Adapter<Json>::append(array, json_scalar<Json>(std::forward<T>(value)));
|
|
}
|
|
}
|
|
template <Json_Type Json, class T>
|
|
T json_get(const Json& value) {
|
|
if constexpr(std::signed_integral<T> && !std::same_as<T, bool>) {
|
|
return static_cast<T>(Json_Adapter<Json>::template get<std::int64_t>(value));
|
|
} else if constexpr(std::unsigned_integral<T> && !std::same_as<T, bool>) {
|
|
return static_cast<T>(Json_Adapter<Json>::template get<std::uint64_t>(value));
|
|
} else if constexpr(std::floating_point<T>) {
|
|
return static_cast<T>(Json_Adapter<Json>::template get<double>(value));
|
|
} else {
|
|
static_assert(Json_Scalar_Gettable<Json, T>, "JSON adapter cannot extract this scalar type");
|
|
return Json_Adapter<Json>::template get<T>(value);
|
|
}
|
|
}
|
|
template <Json_Type Json>
|
|
Json parse_json(std::string_view value) {
|
|
return Json_Adapter<Json>::parse(value);
|
|
}
|
|
template <Json_Type Json>
|
|
std::string dump_json(const Json& value, int indent = -1) {
|
|
return Json_Adapter<Json>::dump(value, indent);
|
|
}
|
|
template <class T, class Json>
|
|
struct Value_Adapter {
|
|
using value_type = std::remove_cvref_t<T>;
|
|
static const value_type& read(const value_type& value) noexcept {
|
|
return value;
|
|
}
|
|
static void write(value_type& target, value_type value) {
|
|
target = std::move(value);
|
|
}
|
|
};
|
|
template <class T, class Json>
|
|
using Adapted_Value_Type = typename Value_Adapter<std::remove_cvref_t<T>, Json>::value_type;
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Encode = requires(const std::remove_cvref_t<T>& value) {
|
|
{ Value_Adapter<std::remove_cvref_t<T>, Json>::encode(value) } -> std::same_as<Json>;
|
|
};
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Decode = requires(std::remove_cvref_t<T>& target, const Json& value) {
|
|
Value_Adapter<std::remove_cvref_t<T>, Json>::decode(target, value);
|
|
};
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Type_Name = requires {
|
|
{ Value_Adapter<std::remove_cvref_t<T>, Json>::type_name } -> std::convertible_to<std::string_view>;
|
|
};
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Schema = requires(Json& value) {
|
|
Value_Adapter<std::remove_cvref_t<T>, Json>::append_schema(value);
|
|
};
|
|
template <class T, class Json>
|
|
decltype(auto) read_adapted_value(const T& value) {
|
|
return Value_Adapter<std::remove_cvref_t<T>, Json>::read(value);
|
|
}
|
|
template <class T, class Json>
|
|
void write_adapted_value(T& target, Adapted_Value_Type<T, Json> value) {
|
|
Value_Adapter<std::remove_cvref_t<T>, Json>::write(target, std::move(value));
|
|
}
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Minimum = requires {
|
|
{ Value_Adapter<std::remove_cvref_t<T>, Json>::minimum };
|
|
};
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Maximum = requires {
|
|
{ Value_Adapter<std::remove_cvref_t<T>, Json>::maximum };
|
|
};
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Multiple_Of = requires {
|
|
{ Value_Adapter<std::remove_cvref_t<T>, Json>::multiple_of };
|
|
};
|
|
template <class T, class Json>
|
|
concept Value_Adapter_With_Validation_Message = requires {
|
|
{ Value_Adapter<std::remove_cvref_t<T>, Json>::validation_message } -> std::convertible_to<std::string_view>;
|
|
};
|
|
template <class T, class Json>
|
|
struct Control_Adapter {};
|
|
struct Control_Context {
|
|
std::string_view permission;
|
|
std::string_view prefix;
|
|
};
|
|
template <class T, class Json>
|
|
concept Control_Adapter_With_Control = requires(const Json& field, Control_Context context) {
|
|
{ Control_Adapter<std::remove_cvref_t<T>, Json>::make_control(field, context) } -> std::same_as<Json>;
|
|
};
|
|
template <class T, class Json>
|
|
concept Control_Adapter_With_Column = requires(const Json& field) {
|
|
{ Control_Adapter<std::remove_cvref_t<T>, Json>::make_column(field) } -> std::same_as<Json>;
|
|
};
|
|
template <class T>
|
|
struct Polymorphic_Variant {
|
|
using type = T;
|
|
std::string value;
|
|
std::string label;
|
|
};
|
|
template <class T>
|
|
auto polymorphic_variant(std::string value, std::string label) {
|
|
return Polymorphic_Variant<T>{std::move(value), std::move(label)};
|
|
}
|
|
template <class T, class Json>
|
|
struct Polymorphic_Adapter;
|
|
template <class T, class Json>
|
|
concept Polymorphic_Type = requires(const std::remove_cvref_t<T>& value, std::remove_cvref_t<T>& target, const Json& input, Write_Context context) {
|
|
{ Polymorphic_Adapter<std::remove_cvref_t<T>, Json>::discriminator() } -> std::convertible_to<std::string_view>;
|
|
{ Polymorphic_Adapter<std::remove_cvref_t<T>, Json>::discriminator_label() } -> std::convertible_to<std::string_view>;
|
|
{ Polymorphic_Adapter<std::remove_cvref_t<T>, Json>::variants() };
|
|
{ Polymorphic_Adapter<std::remove_cvref_t<T>, Json>::encode(value) } -> std::same_as<Json>;
|
|
Polymorphic_Adapter<std::remove_cvref_t<T>, Json>::decode(target, input, context);
|
|
};
|
|
template <class Enum>
|
|
struct Enum_Adapter;
|
|
template <class Enum>
|
|
concept Enum_Type = std::is_enum_v<Enum> && requires(Enum value, std::string_view name) {
|
|
{ Enum_Adapter<Enum>::values() };
|
|
{ Enum_Adapter<Enum>::name(value) } -> std::convertible_to<std::string_view>;
|
|
{ Enum_Adapter<Enum>::cast(name) } -> std::same_as<std::optional<Enum>>;
|
|
};
|
|
template <Enum_Type Enum>
|
|
auto enum_values() {
|
|
return Enum_Adapter<Enum>::values();
|
|
}
|
|
template <Enum_Type Enum>
|
|
std::string_view enum_name(Enum value) {
|
|
return Enum_Adapter<Enum>::name(value);
|
|
}
|
|
template <Enum_Type Enum>
|
|
std::optional<Enum> enum_cast(std::string_view value) {
|
|
return Enum_Adapter<Enum>::cast(value);
|
|
}
|
|
template <class Enum>
|
|
concept Enum_Adapter_With_Underlying_Cast = Enum_Type<Enum> && requires(std::underlying_type_t<Enum> value) {
|
|
{ Enum_Adapter<Enum>::cast(value) } -> std::same_as<std::optional<Enum>>;
|
|
};
|
|
}
|