#pragma once #include "adminive/adapter.hpp" #include "adminive/concepts.hpp" #include #include #include #include #include #include #include #include #include namespace adminive { template struct Type_Descriptor; template concept Described_Type = requires { { Type_Descriptor>::get() }; }; template concept Reflected_Type = requires { { Reflection_Adapter>::field_count } -> std::convertible_to; }; template struct Member_Pointer_Traits; template struct Member_Pointer_Traits { using owner_type = Owner; using member_type = Member_Type; static constexpr auto value = Member; }; template struct Member_Field_Accessor { using owner_type = typename Member_Pointer_Traits::owner_type; using member_type = typename Member_Pointer_Traits::member_type; template static decltype(auto) get(Object& object) noexcept { return object.*Member; } template static decltype(auto) get(const Object& object) noexcept { return object.*Member; } }; template struct Reflected_Field_Accessor { using owner_type = T; using reference_type = decltype(Reflection_Adapter::template get(std::declval())); using const_reference_type = decltype(Reflection_Adapter::template get(std::declval())); using member_type = std::remove_cvref_t; static_assert(std::is_lvalue_reference_v); static_assert(std::is_lvalue_reference_v); static decltype(auto) get(T& object) noexcept(noexcept(Reflection_Adapter::template get(object))) { return Reflection_Adapter::template get(object); } static decltype(auto) get(const T& object) noexcept(noexcept(Reflection_Adapter::template get(object))) { return Reflection_Adapter::template get(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(std::toupper(static_cast(value))) : value); uppercase = false; } return result; } template 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 requires Enum_Type> 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 requires std::convertible_to*, owner_type*> decltype(auto) get(Object& object) const noexcept(noexcept(Accessor::get(object))) { return Accessor::get(object); } template requires std::convertible_to*, 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& 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(*this); } private: std::string name_; std::string label_; std::string list_label_; std::string description_; std::string widget_; std::string visible_on_; std::map enum_labels_; bool editable_{}; bool creatable_{}; bool readable_{true}; bool visible_{true}; bool required_{}; bool list_visible_{true}; int order_{-1}; bool sortable_{}; }; template class Field_Descriptor : public Field_Descriptor_Base, Member_Field_Accessor> { public: using Base = Field_Descriptor_Base, Member_Field_Accessor>; using Base::Base; static constexpr auto member = Member; }; template class Reflected_Field_Descriptor : public Field_Descriptor_Base, Reflected_Field_Accessor> { public: using Base = Field_Descriptor_Base, Reflected_Field_Accessor>; using Base::Base; static constexpr std::size_t index = Index; }; template struct Is_Field_Descriptor : std::false_type {}; template struct Is_Field_Descriptor> : std::true_type {}; template struct Is_Field_Descriptor> : std::true_type {}; template concept Field_Descriptor_Type = Is_Field_Descriptor>::value; template auto field(std::string name) { return Field_Descriptor(std::move(name)); } template auto field(std::string name, std::string label) { return Field_Descriptor(std::move(name), std::move(label)); } template auto reflected_field() { static_assert(Index < static_cast(Reflection_Adapter::field_count)); return Reflected_Field_Descriptor(std::string(Reflection_Adapter::template name())); } template auto reflected_field(std::string label) { static_assert(Index < static_cast(Reflection_Adapter::field_count)); return Reflected_Field_Descriptor(std::string(Reflection_Adapter::template name()), std::move(label)); } struct No_Object_Validator { template 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 Object_Descriptor { public: using object_type = T; using validator_type = Validator; Object_Descriptor(std::string name, std::string label, Validator validator, std::tuple 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 auto validator(New_Validator value) const { return Object_Descriptor(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() 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_; Object_List_Options list_options_; }; template auto object(std::string name, Fields... fields) { static_assert((std::convertible_to && ...)); const auto label = make_label(name); return Object_Descriptor(std::move(name), label, No_Object_Validator{}, std::tuple(std::move(fields)...)); } template auto object(std::string name, std::string label, Fields... fields) { static_assert((std::convertible_to && ...)); return Object_Descriptor(std::move(name), std::move(label), No_Object_Validator{}, std::tuple(std::move(fields)...)); } struct Identity_Field_Customizer { template auto operator()(Field value) const { return value; } }; template auto reflected_object_with_impl(std::string name, std::string label, Customizer customizer, std::index_sequence) { return object(std::move(name), std::move(label), customizer.template operator()(reflected_field())...); } template auto reflected_object_with(std::string name, Customizer customizer) { const auto label = make_label(name); return reflected_object_with_impl(std::move(name), label, std::move(customizer), std::make_index_sequence(Reflection_Adapter::field_count)>{}); } template auto reflected_object_with(std::string name, std::string label, Customizer customizer) { return reflected_object_with_impl(std::move(name), std::move(label), std::move(customizer), std::make_index_sequence(Reflection_Adapter::field_count)>{}); } template auto reflected_object(std::string name) { return reflected_object_with(std::move(name), Identity_Field_Customizer{}); } template auto reflected_object(std::string name, std::string label) { return reflected_object_with(std::move(name), std::move(label), Identity_Field_Customizer{}); } template auto describe() { return Type_Descriptor>::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() #define ADMINIVE_REFLECTED_FIELD_LABEL(Type, Index, Label) ::adminive::reflected_field(Label)