#ifndef RFL_PARSING_NAMEDTUPLEPARSER_HPP_ #define RFL_PARSING_NAMEDTUPLEPARSER_HPP_ #include #include #include #include #include #include "../NamedTuple.hpp" #include "../Result.hpp" #include "../always_false.hpp" #include "../internal/has_default_val_v.hpp" #include "../internal/is_array.hpp" #include "../internal/is_attribute.hpp" #include "../internal/is_basic_type.hpp" #include "../internal/is_default_val_v.hpp" #include "../internal/is_extra_fields.hpp" #include "../internal/is_skip.hpp" #include "../internal/no_duplicate_field_names.hpp" #include "../internal/nth_element_t.hpp" #include "../internal/ptr_cast.hpp" #include "../to_view.hpp" #include "AreReaderAndWriter.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "ViewReader.hpp" #include "ViewReaderWithDefault.hpp" #include "ViewReaderWithDefaultAndStrippedFieldNames.hpp" #include "ViewReaderWithStrippedFieldNames.hpp" #include "call_destructors_where_necessary.hpp" #include "is_empty.hpp" #include "is_required.hpp" #include "schema/Type.hpp" #include "to_single_error_message.hpp" namespace rfl { namespace parsing { template requires AreReaderAndWriter> struct NamedTupleParser { using InputVarType = typename R::InputVarType; using ParentType = Parent; using NamedTupleType = NamedTuple; using ViewReaderType = std::conditional_t< _no_field_names, ViewReaderWithStrippedFieldNames, ViewReader>; using ViewReaderWithDefaultType = std::conditional_t< _no_field_names, ViewReaderWithDefaultAndStrippedFieldNames, ViewReaderWithDefault>; using InputObjectOrArrayType = std::conditional_t<_no_field_names, typename R::InputArrayType, typename R::InputObjectType>; using OutputObjectOrArrayType = std::conditional_t<_no_field_names, typename W::OutputArrayType, typename W::OutputObjectType>; using SchemaType = std::conditional_t<_no_field_names, schema::Type::Tuple, schema::Type::Object>; static constexpr size_t size_ = NamedTupleType::size(); static_assert(NamedTupleType::pos_extra_fields() == -1 || !_no_field_names, "You cannot use the rfl::NoFieldNames processor if you are " "including rfl::ExtraFields."); public: /// The way this works is that we allocate space on the stack in this size of /// the named tuple 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( const R& _r, const InputVarType& _var) noexcept { static_assert( internal::no_duplicate_field_names()); alignas(NamedTuple) unsigned char buf[sizeof(NamedTuple)]; auto ptr = internal::ptr_cast*>(&buf); auto view = rfl::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); } auto res = Result>(std::move(*ptr)); call_destructors_where_necessary(set, &view); return res; } /// Reads the data into a view assuming no default values. static std::pair, std::optional> read_view(const R& _r, const InputVarType& _var, NamedTuple* _view) noexcept { static_assert( internal::no_duplicate_field_names()); if constexpr (_no_field_names) { auto arr = _r.to_array(_var); if (!arr) [[unlikely]] { auto set = std::array{}; return std::make_pair(set, arr.error()); } return read_object_or_array(_r, *arr, _view); } else { auto obj = _r.to_object(_var); if (!obj) [[unlikely]] { auto set = std::array{}; return std::make_pair(set, obj.error()); } return read_object_or_array(_r, *obj, _view); } } /// Reads the data into a view assuming default values. static std::optional read_view_with_default( const R& _r, const InputVarType& _var, NamedTuple* _view) noexcept { static_assert( internal::no_duplicate_field_names()); if constexpr (_no_field_names) { auto arr = _r.to_array(_var); if (!arr) [[unlikely]] { return arr.error(); } return read_object_or_array_with_default(_r, *arr, _view); } else { auto obj = _r.to_object(_var); if (!obj) [[unlikely]] { return obj.error(); } return read_object_or_array_with_default(_r, *obj, _view); } } template static void write(const W& _w, const NamedTuple& _tup, const P& _parent) { if constexpr (_no_field_names) { auto arr = ParentType::add_array(_w, _tup.num_fields(), _parent); build_object(_w, _tup, &arr, std::make_integer_sequence()); _w.end_array(&arr); } else { auto obj = ParentType::add_object(_w, _tup.num_fields(), _parent); build_object(_w, _tup, &obj, std::make_integer_sequence()); _w.end_object(&obj); } } static schema::Type to_schema( std::map* _definitions) noexcept { SchemaType schema; build_schema(_definitions, &schema, std::make_integer_sequence()); return schema::Type{schema}; } private: template static void add_field_to_object(const W& _w, const NamedTuple& _tup, OutputObjectOrArrayType* _ptr) { using FieldType = internal::nth_element_t<_i, FieldTypes...>; using ValueType = std::remove_cvref_t; const auto value = rfl::get<_i>(_tup); if constexpr (internal::is_extra_fields_v) { for (const auto& [k, v] : *value) { const auto new_parent = make_parent(k, _ptr); Parser, ProcessorsType>::write( _w, v, new_parent); } } else if constexpr (!_all_required && !_no_field_names && !is_required()) { constexpr auto name = FieldType::name_.string_view(); const auto new_parent = make_parent(name, _ptr); if (!is_empty(value)) { if constexpr (internal::is_attribute_v) { Parser::write( _w, value, new_parent.as_attribute()); } else { Parser::write(_w, value, new_parent); } } } else { constexpr auto name = FieldType::name_.string_view(); const auto new_parent = make_parent(name, _ptr); if constexpr (internal::is_attribute_v) { Parser::write( _w, value, new_parent.as_attribute()); } else { Parser::write(_w, value, new_parent); } } } template static void add_field_to_schema( std::map* _definitions, SchemaType* _schema) noexcept { using F = internal::nth_element_t<_i, FieldTypes...>; using U = std::remove_cvref_t; if constexpr (!internal::is_skip_v && !internal::is_extra_fields_v) { auto s = Parser::to_schema(_definitions); if constexpr (_no_field_names) { _schema->types_.emplace_back(std::move(s)); } else { _schema->types_[std::string(F::name())] = std::move(s); } } }; template static void build_object(const W& _w, const NamedTuple& _tup, OutputObjectOrArrayType* _ptr, std::integer_sequence) { (add_field_to_object<_is>(_w, _tup, _ptr), ...); } template static void build_schema(std::map* _definitions, SchemaType* _schema, std::integer_sequence) noexcept { (add_field_to_schema<_is>(_definitions, _schema), ...); if constexpr (NamedTupleType::pos_extra_fields() != -1) { using F = internal::nth_element_t; using ExtraFieldsType = std::remove_cvref_t; using U = std::remove_cvref_t; _schema->additional_properties_ = std::make_shared( Parser::to_schema(_definitions)); } } /// Generates error messages for when fields are missing. template static void handle_one_missing_field(const std::array& _found, const NamedTupleType& _view, std::array* _set, std::vector* _errors) noexcept { using FieldType = internal::nth_element_t<_i, FieldTypes...>; using ValueType = std::remove_reference_t< std::remove_pointer_t>; if (!std::get<_i>(_found)) { constexpr bool is_required_field = !internal::is_default_val_v && !internal::is_extra_fields_v && (_all_required || is_required()); if constexpr (is_required_field) { constexpr auto current_name = internal::nth_element_t<_i, FieldTypes...>::name(); std::stringstream stream; stream << "Field named '" << std::string(current_name) << "' not found."; _errors->emplace_back(Error(stream.str())); } else if constexpr (!internal::has_default_val_v) { if constexpr (!std::is_const_v) { ::new (rfl::get<_i>(_view)) ValueType(); } else { using NonConstT = std::remove_const_t; ::new (const_cast(rfl::get<_i>(_view))) NonConstT(); } std::get<_i>(*_set) = true; } } } /// Generates error messages for when fields are missing. template static void handle_missing_fields( const std::array& _found, const NamedTupleType& _view, std::array* _set, std::vector* _errors, std::integer_sequence) noexcept { (handle_one_missing_field<_is>(_found, _view, _set, _errors), ...); } static auto make_parent(const std::string_view& _name, OutputObjectOrArrayType* _ptr) { if constexpr (_no_field_names) { return typename ParentType::Array{_ptr}; } else { return typename ParentType::Object{_name, _ptr}; } } static std::pair, std::optional> read_object_or_array(const R& _r, const InputObjectOrArrayType& _obj_or_arr, NamedTupleType* _view) noexcept { auto found = std::array(); found.fill(false); auto set = std::array(); set.fill(false); std::vector errors; const auto reader = ViewReaderType(&_r, _view, &found, &set, &errors); if constexpr (_no_field_names) { const auto err = _r.read_array(reader, _obj_or_arr); if (err) { return std::make_pair(set, err); } } else { const auto err = _r.read_object(reader, _obj_or_arr); if (err) { return std::make_pair(set, err); } } handle_missing_fields(found, *_view, &set, &errors, std::make_integer_sequence()); if (errors.size() != 0) { return std::make_pair(set, to_single_error_message(errors)); } return std::make_pair(set, std::optional()); } static std::optional read_object_or_array_with_default( const R& _r, const InputObjectOrArrayType& _obj_or_arr, NamedTupleType* _view) noexcept { std::vector errors; const auto reader = ViewReaderWithDefaultType(&_r, _view, &errors); if constexpr (_no_field_names) { const auto err = _r.read_array(reader, _obj_or_arr); if (err) { return err; } } else { const auto err = _r.read_object(reader, _obj_or_arr); if (err) { return err; } } if constexpr (internal::has_default_val_v && !ProcessorsType::default_if_missing_) { handle_missing_fields(reader.found(), *_view, nullptr, &errors, std::make_integer_sequence()); } if (errors.size() != 0) { return to_single_error_message(errors); } return std::nullopt; } }; } // namespace parsing } // namespace rfl #endif