#pragma once #include "adminive/adapter.hpp" #include "adminive/concepts.hpp" #include "adminive/presentation.hpp" #include #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; static constexpr auto member = Member; 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 constexpr std::size_t index = Index; 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 bool valid_data_name(std::string_view name) noexcept { if(name.empty()) { return false; } const auto first = static_cast(name.front()); if(!(std::isalpha(first) || name.front() == '_')) { return false; } for(const char value : name.substr(1)) { const auto character = static_cast(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(std::toupper(static_cast(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 class Field_Descriptor { public: using field_descriptor_tag = void; using accessor_type = Accessor; using owner_type = typename Accessor::owner_type; using member_type = typename Accessor::member_type; static constexpr bool managed_writable = Managed_Writable; static constexpr bool synchronized = Synchronized; explicit Field_Descriptor(std::string name) { metadata_.name = std::move(name); metadata_.presentation.label = make_label(metadata_.name); } Field_Descriptor(std::string name, std::string label) { metadata_.name = std::move(name); metadata_.presentation.label = std::move(label); } explicit Field_Descriptor(Field_Metadata metadata) : metadata_(std::move(metadata)) {} auto editable() const { auto result = rebind(); result.metadata_.editable = true; return result; } auto creatable() const { auto result = rebind(); result.metadata_.creatable = true; return result; } auto read_write() const { return rebind(); } auto unsynchronized() const { return rebind(); } 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 requires Enum_Type> 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 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 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& 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 auto rebind() const { return Field_Descriptor(metadata_); } template friend class Field_Descriptor; Field_Metadata metadata_; }; template concept Field_Descriptor_Type = requires { typename std::remove_cvref_t::field_descriptor_tag; typename std::remove_cvref_t::owner_type; typename std::remove_cvref_t::member_type; typename std::remove_cvref_t::accessor_type; { std::remove_cvref_t::managed_writable } -> std::convertible_to; { std::remove_cvref_t::synchronized } -> std::convertible_to; }; 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)); const std::string name(Reflection_Adapter::template name()); if(name.empty()) { throw std::invalid_argument("reflected field name must not be empty"); } return Field_Descriptor>(name); } template auto reflected_field(std::string label) { static_assert(Index < static_cast(Reflection_Adapter::field_count)); const std::string name(Reflection_Adapter::template name()); if(name.empty()) { throw std::invalid_argument("reflected field name must not be empty"); } return Field_Descriptor>(name, std::move(label)); } struct No_Object_Validator { template void operator()(const T&) const noexcept {} }; template class Object_Descriptor { public: using object_type = T; using validator_type = Validator; using fields_type = std::tuple; Object_Descriptor(std::string name, std::string label, Validator validator, std::tuple fields) : name_(std::move(name)), label_(std::move(label)), validator_(std::move(validator)), fields_(std::move(fields)) {} template auto validator(New_Validator value) const { return Object_Descriptor(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() const noexcept { return fields_; } private: std::string name_; std::string label_; Validator validator_; std::tuple fields_; }; template 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 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 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() { auto descriptor = Type_Descriptor>::get(); validate_descriptor(descriptor); return descriptor; } template using Descriptor_Type = std::remove_cvref_t>::get())>; template using Descriptor_Fields = typename Descriptor_Type::fields_type; template using Descriptor_Field = std::tuple_element_t>; template consteval bool described_type_has_managed_writes(); template consteval bool field_has_managed_writes() { if constexpr(Field::managed_writable) { return true; } else if constexpr(Described_Type) { return described_type_has_managed_writes(); } else { return false; } } template consteval bool described_type_has_managed_writes_impl(std::index_sequence) { return (field_has_managed_writes>() || ...); } template consteval bool described_type_has_managed_writes() { return described_type_has_managed_writes_impl(std::make_index_sequence>>{}); } } #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)