Files
2026-08-12 01:44:13 +08:00

458 lines
18 KiB
C++

#pragma once
#include "adminive/adapter.hpp"
#include "adminive/concepts.hpp"
#include "adminive/presentation.hpp"
#include <structive/property/accessor.hpp>
#include <cctype>
#include <concepts>
#include <map>
#include <set>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
namespace adminive {
template <class T>
struct Type_Descriptor;
template <class T>
concept Described_Type = requires {
{ Type_Descriptor<std::remove_cvref_t<T>>::get() };
};
template <class T>
concept Reflected_Type = requires {
{ Reflection_Adapter<std::remove_cvref_t<T>>::field_count } -> std::convertible_to<std::size_t>;
};
template <class T, std::size_t Index>
struct Reflected_Field_Accessor {
using object_type = T;
using reference_type = decltype(Reflection_Adapter<T>::template get<Index>(std::declval<T&>()));
using const_reference_type = decltype(Reflection_Adapter<T>::template get<Index>(std::declval<const T&>()));
using value_type = std::remove_cvref_t<reference_type>;
struct storage_identity {};
using dependency_spec = structive::No_Property_Dependencies;
static constexpr bool readable = true;
static constexpr bool writable =
!std::is_const_v<std::remove_reference_t<reference_type>>;
static constexpr bool synchronized_view_read = false;
static constexpr bool trusted_object_access = true;
static constexpr std::size_t index = Index;
static_assert(std::is_lvalue_reference_v<reference_type>);
static_assert(std::is_lvalue_reference_v<const_reference_type>);
decltype(auto) read(T& object) const noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
return Reflection_Adapter<T>::template get<Index>(object);
}
decltype(auto) read(const T& object) const noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
return Reflection_Adapter<T>::template get<Index>(object);
}
template <class Value>
void write(T& object, Value&& value) const
requires std::assignable_from<value_type&, Value> {
Reflection_Adapter<T>::template get<Index>(object) = std::forward<Value>(value);
}
};
inline bool valid_data_name(std::string_view name) noexcept {
if(name.empty()) {
return false;
}
const auto first = static_cast<unsigned char>(name.front());
if(!(std::isalpha(first) || name.front() == '_')) {
return false;
}
for(const char value : name.substr(1)) {
const auto character = static_cast<unsigned char>(value);
if(!(std::isalnum(character) || value == '_')) {
return false;
}
}
return true;
}
inline std::string make_label(std::string_view name) {
std::string result;
result.reserve(name.size());
bool uppercase = true;
for(const char value : name) {
if(value == '_') {
result.push_back(' ');
uppercase = true;
continue;
}
result.push_back(uppercase ? static_cast<char>(std::toupper(static_cast<unsigned char>(value))) : value);
uppercase = false;
}
return result;
}
struct Field_Metadata {
std::string name;
Field_Presentation presentation;
bool editable{};
bool creatable{};
bool readable{true};
bool sensitive{};
bool include_default{true};
bool required{};
};
template <structive::Property_Accessor Accessor, bool Managed_Writable = false,
bool Synchronized = true>
class Field_Descriptor {
public:
using field_descriptor_tag = void;
using accessor_type = Accessor;
using owner_type = typename Accessor::object_type;
using member_type = typename Accessor::value_type;
static constexpr bool managed_writable = Managed_Writable;
static constexpr bool synchronized = Synchronized;
Field_Descriptor(std::string name, Accessor accessor = {})
: accessor_(std::move(accessor)) {
metadata_.name = std::move(name);
metadata_.presentation.label = make_label(metadata_.name);
}
Field_Descriptor(std::string name, std::string label, Accessor accessor = {})
: accessor_(std::move(accessor)) {
metadata_.name = std::move(name);
metadata_.presentation.label = std::move(label);
}
Field_Descriptor(Field_Metadata metadata, Accessor accessor)
: accessor_(std::move(accessor)), metadata_(std::move(metadata)) {}
auto editable() const requires Accessor::writable {
auto result = rebind<true, Synchronized>();
result.metadata_.editable = true;
return result;
}
auto creatable() const requires Accessor::writable {
auto result = rebind<true, Synchronized>();
result.metadata_.creatable = true;
return result;
}
auto read_write() const requires Accessor::writable {
return rebind<true, Synchronized>();
}
auto unsynchronized() const {
return rebind<Managed_Writable, false>();
}
Field_Descriptor readable(bool value = true) const {
auto result = *this;
result.metadata_.readable = value;
return result;
}
Field_Descriptor sensitive(bool value = true) const {
auto result = *this;
result.metadata_.sensitive = value;
if(value) {
result.metadata_.readable = false;
result.metadata_.include_default = false;
}
return result;
}
Field_Descriptor include_default(bool value = true) const {
auto result = *this;
result.metadata_.include_default = value;
return result;
}
Field_Descriptor required(bool value = true) const {
auto result = *this;
result.metadata_.required = value;
return result;
}
Field_Descriptor label(std::string value) const {
auto result = *this;
result.metadata_.presentation.label = std::move(value);
return result;
}
Field_Descriptor description(std::string value) const {
auto result = *this;
result.metadata_.presentation.description = std::move(value);
return result;
}
Field_Descriptor control(Field_Control value) const {
auto result = *this;
result.metadata_.presentation.control = value;
return result;
}
Field_Descriptor text_input() const {
return control(Field_Control::text);
}
Field_Descriptor multiline_text() const {
return control(Field_Control::multiline_text);
}
Field_Descriptor number_input() const {
return control(Field_Control::number);
}
Field_Descriptor boolean_input() const {
return control(Field_Control::boolean);
}
Field_Descriptor select_input() const {
return control(Field_Control::select);
}
Field_Descriptor date_input() const {
return control(Field_Control::date);
}
Field_Descriptor color_input() const {
return control(Field_Control::color);
}
Field_Descriptor visible_on(std::string value) const {
auto result = *this;
result.metadata_.presentation.visible_on = std::move(value);
return result;
}
template <auto Value>
requires Enum_Type<std::remove_cv_t<decltype(Value)>>
Field_Descriptor enum_label(std::string label) const {
const auto name = enum_name(Value);
if(name.empty()) {
throw std::invalid_argument("enum adapter returned an empty value name");
}
auto result = *this;
result.metadata_.presentation.enum_labels.insert_or_assign(std::string(name), std::move(label));
return result;
}
Field_Descriptor enum_label(std::string value_name, std::string label) const {
auto result = *this;
result.metadata_.presentation.enum_labels.insert_or_assign(std::move(value_name), std::move(label));
return result;
}
template <class Object>
requires std::convertible_to<std::remove_reference_t<Object>*, owner_type*>
decltype(auto) get(Object& object) const
noexcept(noexcept(accessor_.read(object))) {
return accessor_.read(object);
}
template <class Object>
requires std::convertible_to<const std::remove_reference_t<Object>*, const owner_type*>
decltype(auto) get(const Object& object) const
noexcept(noexcept(accessor_.read(object))) {
return accessor_.read(object);
}
template <class Object, class Value>
requires std::convertible_to<std::remove_reference_t<Object>*, owner_type*> &&
requires(const Accessor& accessor, Object& object, Value&& value) {
accessor.write(object, std::forward<Value>(value));
}
void set(Object& object, Value&& value) const
noexcept(noexcept(accessor_.write(object, std::forward<Value>(value)))) {
accessor_.write(object, std::forward<Value>(value));
}
const std::string& name() const noexcept {
return metadata_.name;
}
const Field_Presentation& presentation() const noexcept {
return metadata_.presentation;
}
const std::string& label() const noexcept {
return metadata_.presentation.label;
}
const std::string& description() const noexcept {
return metadata_.presentation.description;
}
Field_Control control() const noexcept {
return metadata_.presentation.control;
}
const std::string& visible_on() const noexcept {
return metadata_.presentation.visible_on;
}
const std::map<std::string, std::string>& enum_labels() const noexcept {
return metadata_.presentation.enum_labels;
}
bool is_editable() const noexcept {
return metadata_.editable;
}
bool is_creatable() const noexcept {
return metadata_.creatable;
}
bool is_readable() const noexcept {
return metadata_.readable;
}
bool is_sensitive() const noexcept {
return metadata_.sensitive;
}
bool includes_default() const noexcept {
return metadata_.include_default;
}
bool is_required() const noexcept {
return metadata_.required;
}
private:
template <bool New_Writable, bool New_Synchronized>
auto rebind() const {
return Field_Descriptor<Accessor, New_Writable, New_Synchronized>(metadata_, accessor_);
}
template <structive::Property_Accessor, bool, bool>
friend class Field_Descriptor;
[[no_unique_address]] Accessor accessor_;
Field_Metadata metadata_;
};
template <class T>
concept Field_Descriptor_Type = requires {
typename std::remove_cvref_t<T>::field_descriptor_tag;
typename std::remove_cvref_t<T>::owner_type;
typename std::remove_cvref_t<T>::member_type;
typename std::remove_cvref_t<T>::accessor_type;
{ std::remove_cvref_t<T>::managed_writable } -> std::convertible_to<bool>;
{ std::remove_cvref_t<T>::synchronized } -> std::convertible_to<bool>;
};
template <auto Member>
auto field(std::string name) {
return Field_Descriptor<structive::Member_Accessor<Member>>(std::move(name));
}
template <auto Member>
auto field(std::string name, std::string label) {
return Field_Descriptor<structive::Member_Accessor<Member>>(std::move(name), std::move(label));
}
template <structive::Property_Accessor Accessor>
auto field(std::string name, Accessor accessor) {
return Field_Descriptor<Accessor>(std::move(name), std::move(accessor));
}
template <structive::Property_Accessor Accessor>
auto field(std::string name, std::string label, Accessor accessor) {
return Field_Descriptor<Accessor>(std::move(name), std::move(label), std::move(accessor));
}
template <Reflected_Type T, std::size_t Index>
auto reflected_field() {
static_assert(Index < static_cast<std::size_t>(Reflection_Adapter<T>::field_count));
const std::string name(Reflection_Adapter<T>::template name<Index>());
if(name.empty()) {
throw std::invalid_argument("reflected field name must not be empty");
}
return Field_Descriptor<Reflected_Field_Accessor<T, Index>>(name);
}
template <Reflected_Type T, std::size_t Index>
auto reflected_field(std::string label) {
static_assert(Index < static_cast<std::size_t>(Reflection_Adapter<T>::field_count));
const std::string name(Reflection_Adapter<T>::template name<Index>());
if(name.empty()) {
throw std::invalid_argument("reflected field name must not be empty");
}
return Field_Descriptor<Reflected_Field_Accessor<T, Index>>(name, std::move(label));
}
struct No_Object_Validator {
template <class T>
void operator()(const T&) const noexcept {}
};
template <class T, class Validator, Field_Descriptor_Type... Fields>
class Object_Descriptor {
public:
using object_type = T;
using validator_type = Validator;
using fields_type = std::tuple<Fields...>;
Object_Descriptor(std::string name, std::string label, Validator validator, std::tuple<Fields...> fields) : name_(std::move(name)), label_(std::move(label)), validator_(std::move(validator)), fields_(std::move(fields)) {}
template <class New_Validator>
auto validator(New_Validator value) const {
return Object_Descriptor<T, New_Validator, Fields...>(name_, label_, std::move(value), fields_);
}
Object_Descriptor label(std::string value) const {
auto result = *this;
result.label_ = std::move(value);
return result;
}
const std::string& name() const noexcept {
return name_;
}
const std::string& label() const noexcept {
return label_;
}
const Validator& object_validator() const noexcept {
return validator_;
}
const std::tuple<Fields...>& fields() const noexcept {
return fields_;
}
private:
std::string name_;
std::string label_;
Validator validator_;
std::tuple<Fields...> fields_;
};
template <class Descriptor>
void validate_descriptor(const Descriptor& descriptor) {
if(!valid_data_name(descriptor.name())) {
throw std::invalid_argument("descriptor name must match [A-Za-z_][A-Za-z0-9_]*: " + descriptor.name());
}
std::set<std::string> field_names;
std::apply([&](const auto&... field) {
([&] {
if(!valid_data_name(field.name())) {
throw std::invalid_argument("descriptor field name must match [A-Za-z_][A-Za-z0-9_]*: " + field.name());
}
if(!field_names.insert(field.name()).second) {
throw std::invalid_argument("duplicate descriptor field name: " + field.name());
}
}(), ...);
}, descriptor.fields());
}
template <class T, Field_Descriptor_Type... Fields>
auto object(std::string name, Fields... fields) {
static_assert((std::convertible_to<T*, typename Fields::owner_type*> && ...));
const auto label = make_label(name);
return Object_Descriptor<T, No_Object_Validator, Fields...>(std::move(name), label, No_Object_Validator{}, std::tuple<Fields...>(std::move(fields)...));
}
template <class T, Field_Descriptor_Type... Fields>
auto object(std::string name, std::string label, Fields... fields) {
static_assert((std::convertible_to<T*, typename Fields::owner_type*> && ...));
return Object_Descriptor<T, No_Object_Validator, Fields...>(std::move(name), std::move(label), No_Object_Validator{}, std::tuple<Fields...>(std::move(fields)...));
}
struct Identity_Field_Customizer {
template <std::size_t Index, Field_Descriptor_Type Field>
auto operator()(Field value) const {
return value;
}
};
template <Reflected_Type T, class Customizer, std::size_t... Indexes>
auto reflected_object_with_impl(std::string name, std::string label, Customizer customizer, std::index_sequence<Indexes...>) {
return adminive::object<T>(std::move(name), std::move(label), customizer.template operator()<Indexes>(reflected_field<T, Indexes>())...);
}
template <Reflected_Type T, class Customizer>
auto reflected_object_with(std::string name, Customizer customizer) {
const auto label = make_label(name);
return reflected_object_with_impl<T>(std::move(name), label, std::move(customizer), std::make_index_sequence<static_cast<std::size_t>(Reflection_Adapter<T>::field_count)>{});
}
template <Reflected_Type T, class Customizer>
auto reflected_object_with(std::string name, std::string label, Customizer customizer) {
return reflected_object_with_impl<T>(std::move(name), std::move(label), std::move(customizer), std::make_index_sequence<static_cast<std::size_t>(Reflection_Adapter<T>::field_count)>{});
}
template <Reflected_Type T>
auto reflected_object(std::string name) {
return reflected_object_with<T>(std::move(name), Identity_Field_Customizer{});
}
template <Reflected_Type T>
auto reflected_object(std::string name, std::string label) {
return reflected_object_with<T>(std::move(name), std::move(label), Identity_Field_Customizer{});
}
template <Described_Type T>
auto describe() {
auto descriptor = Type_Descriptor<std::remove_cvref_t<T>>::get();
validate_descriptor(descriptor);
return descriptor;
}
template <class T>
using Descriptor_Type = std::remove_cvref_t<decltype(Type_Descriptor<std::remove_cvref_t<T>>::get())>;
template <class T>
using Descriptor_Fields = typename Descriptor_Type<T>::fields_type;
template <class T, std::size_t Index>
using Descriptor_Field = std::tuple_element_t<Index, Descriptor_Fields<T>>;
template <Described_Type T>
consteval bool described_type_has_managed_writes();
template <class Field>
consteval bool field_has_managed_writes() {
if constexpr(Field::managed_writable) {
return true;
} else if constexpr(Described_Type<typename Field::member_type>) {
return described_type_has_managed_writes<typename Field::member_type>();
} else {
return false;
}
}
template <Described_Type T, std::size_t... Indexes>
consteval bool described_type_has_managed_writes_impl(std::index_sequence<Indexes...>) {
return (field_has_managed_writes<Descriptor_Field<T, Indexes>>() || ...);
}
template <Described_Type T>
consteval bool described_type_has_managed_writes() {
return described_type_has_managed_writes_impl<T>(std::make_index_sequence<std::tuple_size_v<Descriptor_Fields<T>>>{});
}
}
#define ADMINIVE_FIELD(Type, Member) ::adminive::field<&Type::Member>(#Member)
#define ADMINIVE_FIELD_LABEL(Type, Member, Label) ::adminive::field<&Type::Member>(#Member, Label)
#define ADMINIVE_REFLECTED_FIELD(Type, Index) ::adminive::reflected_field<Type, Index>()
#define ADMINIVE_REFLECTED_FIELD_LABEL(Type, Index, Label) ::adminive::reflected_field<Type, Index>(Label)