#ifndef RFL_PARSING_PARSER_DEFAULT_HPP_ #define RFL_PARSING_PARSER_DEFAULT_HPP_ #include #include #include #include "../Result.hpp" #include "../always_false.hpp" #include "../from_named_tuple.hpp" #include "../internal/enums/StringConverter.hpp" #include "../internal/has_reflection_method_v.hpp" #include "../internal/has_reflection_type_v.hpp" #include "../internal/is_basic_type.hpp" #include "../internal/is_description.hpp" #include "../internal/is_literal.hpp" #include "../internal/is_validator.hpp" #include "../internal/processed_t.hpp" #include "../internal/to_ptr_named_tuple.hpp" #include "../to_view.hpp" #include "../type_name_t.hpp" #include "AreReaderAndWriter.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "is_tagged_union_wrapper.hpp" #include "schema/Type.hpp" namespace rfl { namespace parsing { /// Default case - anything that cannot be explicitly matched. template requires AreReaderAndWriter struct Parser { public: using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; using ParentType = Parent; /// Expresses the variables as type T. static Result read(const R& _r, const InputVarType& _var) noexcept { if constexpr (R::template has_custom_constructor) { return _r.template use_custom_constructor(_var); } else { if constexpr (internal::has_reflection_type_v) { using ReflectionType = std::remove_cvref_t; const auto wrap_in_t = [](auto _named_tuple) -> Result { try { return T(_named_tuple); } catch (std::exception& e) { return Error(e.what()); } }; return Parser::read(_r, _var).and_then(wrap_in_t); } else if constexpr (std::is_class_v && std::is_aggregate_v) { return read_struct(_r, _var); } else if constexpr (std::is_enum_v) { using StringConverter = internal::enums::StringConverter; return _r.template to_basic_type(_var).and_then( StringConverter::string_to_enum ); } else { return _r.template to_basic_type>(_var); } } } template static void write(const W& _w, const T& _var, const P& _parent) noexcept { if constexpr (internal::has_reflection_type_v) { using ReflectionType = std::remove_cvref_t; if constexpr (internal::has_reflection_method_v) { Parser::write(_w, _var.reflection(), _parent); } else { const auto& [r] = _var; Parser::write(_w, r, _parent); } } else if constexpr (std::is_class_v && std::is_aggregate_v) { const auto ptr_named_tuple = ProcessorsType::template process(internal::to_ptr_named_tuple(_var)); using PtrNamedTupleType = std::remove_cvref_t; Parser::write(_w, ptr_named_tuple, _parent); } else if constexpr (std::is_enum_v) { using StringConverter = internal::enums::StringConverter; const auto str = StringConverter::enum_to_string(_var); ParentType::add_value(_w, str, _parent); } else { ParentType::add_value(_w, _var, _parent); } } /// Generates a schema for the underlying type. static schema::Type to_schema(std::map* _definitions) { using U = std::remove_cvref_t; using Type = schema::Type; if constexpr (std::is_same()) { return Type { Type::Boolean {} }; } else if constexpr (std::is_same()) { return Type { Type::Int32 {} }; } else if constexpr (std::is_same()) { return Type { Type::Int64 {} }; } else if constexpr (std::is_same()) { return Type { Type::UInt32 {} }; } else if constexpr (std::is_same()) { return Type { Type::UInt64 {} }; } else if constexpr (std::is_integral()) { return Type { Type::Integer {} }; } else if constexpr (std::is_same()) { return Type { Type::Float {} }; } else if constexpr (std::is_floating_point_v) { return Type { Type::Double {} }; } else if constexpr (std::is_same()) { return Type { Type::String {} }; } else if constexpr (rfl::internal::is_description_v) { return make_description(_definitions); } else if constexpr (std::is_enum_v) { return make_enum(_definitions); } else if constexpr (std::is_class_v && std::is_aggregate_v) { return make_reference(_definitions); } else if constexpr (internal::is_literal_v) { return Type { Type::Literal { .values_ = U::strings() } }; } else if constexpr (internal::is_validator_v) { return make_validated(_definitions); } else if constexpr (internal::has_reflection_type_v) { return Parser::to_schema(_definitions); } else { static_assert(rfl::always_false_v, "Unsupported type."); } } private: template static schema::Type make_description(std::map* _definitions) { using Type = schema::Type; return Type { Type::Description { .description_ = typename U::Content().str(), .type_ = Ref::make( Parser, ProcessorsType>::to_schema( _definitions ) ) } }; } template static schema::Type make_enum(std::map* _definitions) { using Type = schema::Type; using S = internal::enums::StringConverter; if constexpr (S::is_flag_enum_) { return Type { Type::String {} }; } else { return Parser::to_schema(_definitions); } } template static schema::Type make_reference(std::map* _definitions) { using Type = schema::Type; const auto name = make_type_name(); if (_definitions->find(name) == _definitions->end()) { (*_definitions)[name] = Type { Type::Integer {} }; // Placeholder to avoid infinite loop. using NamedTupleType = internal::processed_t; (*_definitions)[name] = Parser::to_schema(_definitions); } return Type { Type::Reference { name } }; } template static schema::Type make_validated(std::map* _definitions) { using Type = schema::Type; using ReflectionType = std::remove_cvref_t; using ValidationType = std::remove_cvref_t; return Type { Type::Validated { .type_ = Ref::make( Parser::to_schema(_definitions) ), .validation_ = ValidationType::template to_schema() } }; } template static std::string make_type_name() { if constexpr (is_tagged_union_wrapper_v) { return replace_non_alphanumeric(type_name_t().str() + "__tagged"); } else { return replace_non_alphanumeric(type_name_t().str()); } } /// The way this works is that we allocate space on the stack in this size /// of the struct in which we then write the individual fields using views /// and placement new. This is how we deal with the fact that some fields /// might not be default-constructible. static Result read_struct(const R& _r, const InputVarType& _var) { alignas(T) unsigned char buf[sizeof(T)]; auto ptr = reinterpret_cast(buf); auto view = ProcessorsType::template process(to_view(*ptr)); using ViewType = std::remove_cvref_t; const auto err = Parser::read_view(_r, _var, &view); if (err) [[unlikely]] { return *err; } return std::move(*ptr); } static std::string replace_non_alphanumeric(std::string _str) { for (auto& ch : _str) { ch = std::isalnum(ch) ? ch : '_'; } return _str; } }; } // namespace parsing } // namespace rfl #endif