#ifndef RFL_PARSING_PARSER_DEFAULT_HPP_ #define RFL_PARSING_PARSER_DEFAULT_HPP_ #include #include #include "../Result.hpp" #include "../always_false.hpp" #include "../enums.hpp" #include "../from_named_tuple.hpp" #include "../internal/has_default_val_v.hpp" #include "../internal/has_reflection_method_v.hpp" #include "../internal/has_reflection_type_v.hpp" #include "../internal/has_reflector.hpp" #include "../internal/is_basic_type.hpp" #include "../internal/is_description.hpp" #include "../internal/is_literal.hpp" #include "../internal/is_underlying_enums_v.hpp" #include "../internal/is_validator.hpp" #include "../internal/processed_t.hpp" #include "../internal/ptr_cast.hpp" #include "../internal/to_ptr_named_tuple.hpp" #include "../thirdparty/enchantum/enchantum.hpp" #include "../to_view.hpp" #include "AreReaderAndWriter.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "call_destructors_where_necessary.hpp" #include "is_tagged_union_wrapper.hpp" #include "make_type_name.hpp" #include "schema/Type.hpp" #include "schemaful/IsSchemafulReader.hpp" #include "schemaful/IsSchemafulWriter.hpp" namespace rfl::parsing { /// Default case - anything that cannot be explicitly matched. template requires AreReaderAndWriter struct Parser { public: using InputVarType = typename R::InputVarType; using ParentType = Parent; /// Expresses the variables as type T. static Result read(const R& _r, const InputVarType& _var) noexcept { if constexpr (internal::has_read_reflector) { const auto wrap_in_t = [](auto&& _named_tuple) -> Result { try { using NT = decltype(_named_tuple); return Reflector::to(std::forward(_named_tuple)); } catch (std::exception& e) { return error(e.what()); } }; return Parser::ReflType, ProcessorsType>::read(_r, _var) .and_then(wrap_in_t); } else if constexpr (schemaful::IsSchemafulReader && internal::is_literal_v) { return _r.template to_basic_type(_var); } else 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 { using NT = decltype(_named_tuple); return T{std::forward(_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) { if constexpr (ProcessorsType::default_if_missing_ || internal::has_default_val_v) { return read_struct_with_default(_r, _var); } else { return read_struct(_r, _var); } } else if constexpr (std::is_enum_v) { if constexpr (ProcessorsType::underlying_enums_ || schemaful::IsSchemafulReader) { static_assert(enchantum::ScopedEnum, "The enum must be a scoped enum in order to retrieve " "the underlying value."); return _r.template to_basic_type>(_var) .transform([](const auto _val) { return static_cast(_val); }); } else { return _r.template to_basic_type(_var).and_then( rfl::string_to_enum); } } else { return _r.template to_basic_type>(_var); } } } template static void write(const W& _w, const T& _var, const P& _parent) { if constexpr (internal::has_write_reflector) { Parser::ReflType, ProcessorsType>::write( _w, Reflector::from(_var), _parent); } else if constexpr (schemaful::IsSchemafulWriter && internal::is_literal_v) { ParentType::add_value(_w, _var, _parent); } else 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) { if constexpr (ProcessorsType::underlying_enums_ || schemaful::IsSchemafulWriter) { const auto val = static_cast>(_var); ParentType::add_value(_w, val, _parent); } else { const auto str = rfl::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 || internal::has_read_reflector || internal::has_write_reflector) { return make_reference(_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; if constexpr (ProcessorsType::underlying_enums_ || schemaful::IsSchemafulReader) { return Type{Type::Integer{}}; } else if constexpr (enchantum::is_bitflag) { return Type{Type::String{}}; } else { return Parser< R, W, typename decltype(internal::enums::get_enum_names())::Literal, ProcessorsType>::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. if constexpr (internal::has_reflection_type_v) { (*_definitions)[name] = Parser::to_schema( _definitions); } else if constexpr (internal::has_read_reflector || internal::has_write_reflector) { (*_definitions)[name] = Parser::ReflType, ProcessorsType>::to_schema(_definitions); } else { 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()}}; } /// 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 = internal::ptr_cast(&buf); auto view = ProcessorsType::template process(to_view(*ptr)); using ViewType = std::remove_cvref_t; const auto [set, err] = Parser::read_view(_r, _var, &view); if (err) [[unlikely]] { call_destructors_where_necessary(set, &view); return error(err->what()); } auto res = Result(std::move(*ptr)); call_destructors_where_necessary(set, &view); return res; } /// This is actually more straight-forward than the standard case - we just /// allocate a struct and then fill it. But it is less efficient and it /// assumes that all values on the struct have a default constructor, so we /// only use it when the DefaultIfMissing preprocessor is added. static Result read_struct_with_default(const R& _r, const InputVarType& _var) { auto t = T{}; auto view = ProcessorsType::template process(to_view(t)); using ViewType = decltype(view); const auto err = Parser::read_view_with_default(_r, _var, &view); if (err) [[unlikely]] { return error(*err); } return t; } }; } // namespace rfl::parsing #endif