Files
Adminive/backend/include/adminive/descriptor.hpp
T
2026-08-06 20:33:28 +08:00

443 lines
17 KiB
C++

#pragma once
#include "adminive/adapter.hpp"
#include "adminive/concepts.hpp"
#include <cctype>
#include <concepts>
#include <map>
#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 <auto Member>
struct Member_Pointer_Traits;
template <class Owner, class Member_Type, Member_Type Owner::*Member>
struct Member_Pointer_Traits<Member> {
using owner_type = Owner;
using member_type = Member_Type;
static constexpr auto value = Member;
};
template <auto Member>
struct Member_Field_Accessor {
using owner_type = typename Member_Pointer_Traits<Member>::owner_type;
using member_type = typename Member_Pointer_Traits<Member>::member_type;
template <class Object>
static decltype(auto) get(Object& object) noexcept {
return object.*Member;
}
template <class Object>
static decltype(auto) get(const Object& object) noexcept {
return object.*Member;
}
};
template <class T, std::size_t Index>
struct Reflected_Field_Accessor {
using owner_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 member_type = std::remove_cvref_t<reference_type>;
static_assert(std::is_lvalue_reference_v<reference_type>);
static_assert(std::is_lvalue_reference_v<const_reference_type>);
static decltype(auto) get(T& object) noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
return Reflection_Adapter<T>::template get<Index>(object);
}
static decltype(auto) get(const T& object) noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
return Reflection_Adapter<T>::template get<Index>(object);
}
};
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;
}
template <class Derived, class Accessor>
class Field_Descriptor_Base {
public:
using owner_type = typename Accessor::owner_type;
using member_type = typename Accessor::member_type;
explicit Field_Descriptor_Base(std::string name) : name_(std::move(name)), label_(make_label(name_)) {}
Field_Descriptor_Base(std::string name, std::string label) : name_(std::move(name)), label_(std::move(label)) {}
Derived editable(bool value = true) const {
auto result = derived();
result.editable_ = value;
return result;
}
Derived creatable(bool value = true) const {
auto result = derived();
result.creatable_ = value;
return result;
}
Derived readable(bool value = true) const {
auto result = derived();
result.readable_ = value;
return result;
}
Derived visible(bool value = true) const {
auto result = derived();
result.visible_ = value;
return result;
}
Derived required(bool value = true) const {
auto result = derived();
result.required_ = value;
return result;
}
Derived list_visible(bool value = true) const {
auto result = derived();
result.list_visible_ = value;
return result;
}
Derived label(std::string value) const {
auto result = derived();
result.label_ = std::move(value);
return result;
}
Derived description(std::string value) const {
auto result = derived();
result.description_ = std::move(value);
return result;
}
Derived widget(std::string value) const {
auto result = derived();
result.widget_ = std::move(value);
return result;
}
Derived visible_on(std::string value) const {
auto result = derived();
result.visible_on_ = std::move(value);
return result;
}
Derived list_label(std::string value) const {
auto result = derived();
result.list_label_ = std::move(value);
return result;
}
Derived order(int value) const {
auto result = derived();
result.order_ = value;
return result;
}
Derived sortable(bool value = true) const {
auto result = derived();
result.sortable_ = value;
return result;
}
template <auto Value>
requires Enum_Type<std::remove_cv_t<decltype(Value)>>
Derived 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 = derived();
result.enum_labels_.insert_or_assign(std::string(name), std::move(label));
return result;
}
Derived enum_label(std::string value_name, std::string label) const {
auto result = derived();
result.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::get(object))) {
return Accessor::get(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::get(object))) {
return Accessor::get(object);
}
const std::string& name() const noexcept {
return name_;
}
const std::string& label() const noexcept {
return label_;
}
const std::string& list_label() const noexcept {
return list_label_.empty() ? label_ : list_label_;
}
const std::string& description() const noexcept {
return description_;
}
const std::string& widget() const noexcept {
return widget_;
}
const std::string& visible_on() const noexcept {
return visible_on_;
}
const std::map<std::string, std::string>& enum_labels() const noexcept {
return enum_labels_;
}
bool is_editable() const noexcept {
return editable_;
}
bool is_creatable() const noexcept {
return creatable_;
}
bool is_readable() const noexcept {
return readable_;
}
bool is_visible() const noexcept {
return visible_;
}
bool is_required() const noexcept {
return required_;
}
int order() const noexcept {
return order_;
}
bool is_sortable() const noexcept {
return sortable_;
}
bool is_list_visible() const noexcept {
return list_visible_;
}
protected:
const Derived& derived() const noexcept {
return static_cast<const Derived&>(*this);
}
private:
std::string name_;
std::string label_;
std::string list_label_;
std::string description_;
std::string widget_;
std::string visible_on_;
std::map<std::string, std::string> enum_labels_;
bool editable_{};
bool creatable_{};
bool readable_{true};
bool visible_{true};
bool required_{};
bool list_visible_{true};
int order_{-1};
bool sortable_{};
};
template <auto Member>
class Field_Descriptor : public Field_Descriptor_Base<Field_Descriptor<Member>, Member_Field_Accessor<Member>> {
public:
using Base = Field_Descriptor_Base<Field_Descriptor<Member>, Member_Field_Accessor<Member>>;
using Base::Base;
static constexpr auto member = Member;
};
template <class T, std::size_t Index>
class Reflected_Field_Descriptor : public Field_Descriptor_Base<Reflected_Field_Descriptor<T, Index>, Reflected_Field_Accessor<T, Index>> {
public:
using Base = Field_Descriptor_Base<Reflected_Field_Descriptor<T, Index>, Reflected_Field_Accessor<T, Index>>;
using Base::Base;
static constexpr std::size_t index = Index;
};
template <class T>
struct Is_Field_Descriptor : std::false_type {};
template <auto Member>
struct Is_Field_Descriptor<Field_Descriptor<Member>> : std::true_type {};
template <class T, std::size_t Index>
struct Is_Field_Descriptor<Reflected_Field_Descriptor<T, Index>> : std::true_type {};
template <class T>
concept Field_Descriptor_Type = Is_Field_Descriptor<std::remove_cvref_t<T>>::value;
template <auto Member>
auto field(std::string name) {
return Field_Descriptor<Member>(std::move(name));
}
template <auto Member>
auto field(std::string name, std::string label) {
return Field_Descriptor<Member>(std::move(name), std::move(label));
}
template <Reflected_Type T, std::size_t Index>
auto reflected_field() {
static_assert(Index < static_cast<std::size_t>(Reflection_Adapter<T>::field_count));
return Reflected_Field_Descriptor<T, Index>(std::string(Reflection_Adapter<T>::template name<Index>()));
}
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));
return Reflected_Field_Descriptor<T, Index>(std::string(Reflection_Adapter<T>::template name<Index>()), std::move(label));
}
struct No_Object_Validator {
template <class T>
void operator()(const T&) const noexcept {}
};
struct Object_List_Options {
std::string id_label{"ID"};
std::string create_label{"Create"};
std::string edit_label{"Edit"};
std::string delete_label{"Delete"};
std::string actions_label{"Actions"};
std::string confirm_label{"Confirm Changes"};
std::string delete_confirm{"Delete this record?"};
std::string view_status_label{"View status"};
std::string management_label;
std::string default_order_by;
std::string default_order_dir{"asc"};
bool user_reorderable_{};
bool column_reorderable_{true};
};
template <class T, class Validator, Field_Descriptor_Type... Fields>
class Object_Descriptor {
public:
using object_type = T;
using validator_type = Validator;
Object_Descriptor(std::string name, std::string label, Validator validator, std::tuple<Fields...> fields, Object_List_Options list_options = {}) : name_(std::move(name)), label_(std::move(label)), validator_(std::move(validator)), fields_(std::move(fields)), list_options_(std::move(list_options)) {}
template <class New_Validator>
auto validator(New_Validator value) const {
return Object_Descriptor<T, New_Validator, Fields...>(name_, label_, std::move(value), fields_, list_options_);
}
Object_Descriptor label(std::string value) const {
auto result = *this;
result.label_ = std::move(value);
return result;
}
Object_Descriptor id_label(std::string value) const {
auto result = *this;
result.list_options_.id_label = std::move(value);
return result;
}
Object_Descriptor create_label(std::string value) const {
auto result = *this;
result.list_options_.create_label = std::move(value);
return result;
}
Object_Descriptor edit_label(std::string value) const {
auto result = *this;
result.list_options_.edit_label = std::move(value);
return result;
}
Object_Descriptor delete_label(std::string value) const {
auto result = *this;
result.list_options_.delete_label = std::move(value);
return result;
}
Object_Descriptor actions_label(std::string value) const {
auto result = *this;
result.list_options_.actions_label = std::move(value);
return result;
}
Object_Descriptor confirm_label(std::string value) const {
auto result = *this;
result.list_options_.confirm_label = std::move(value);
return result;
}
Object_Descriptor delete_confirm(std::string value) const {
auto result = *this;
result.list_options_.delete_confirm = std::move(value);
return result;
}
Object_Descriptor view_status_label(std::string value) const {
auto result = *this;
result.list_options_.view_status_label = std::move(value);
return result;
}
Object_Descriptor management_label(std::string value) const {
auto result = *this;
result.list_options_.management_label = std::move(value);
return result;
}
Object_Descriptor default_sort(std::string field_name, bool descending = false) const {
auto result = *this;
result.list_options_.default_order_by = std::move(field_name);
result.list_options_.default_order_dir = descending ? "desc" : "asc";
return result;
}
Object_Descriptor user_reorderable(bool value = true) const {
auto result = *this;
result.list_options_.user_reorderable_ = value;
return result;
}
Object_Descriptor column_reorderable(bool value = true) const {
auto result = *this;
result.list_options_.column_reorderable_ = 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_;
}
const Object_List_Options& list_options() const noexcept {
return list_options_;
}
private:
std::string name_;
std::string label_;
Validator validator_;
std::tuple<Fields...> fields_;
Object_List_Options list_options_;
};
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 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() {
return Type_Descriptor<std::remove_cvref_t<T>>::get();
}
}
#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)