#ifndef RFL_PARSING_VIEWREADER_HPP_ #define RFL_PARSING_VIEWREADER_HPP_ #include #include #include #include #include #include #include "../Result.hpp" #include "../Tuple.hpp" #include "../internal/is_array.hpp" #include "Parser_base.hpp" #include "schemaful/IsSchemafulReader.hpp" namespace rfl::parsing { template class ViewReader { private: using InputVarType = typename R::InputVarType; static constexpr size_t size_ = ViewType::size(); public: ViewReader(const R* _r, ViewType* _view, std::array* _found, std::array* _set, std::vector* _errors) : r_(_r), view_(_view), found_(_found), set_(_set), errors_(_errors) {} ~ViewReader() = default; /// Assigns the parsed version of _var to the field signified by _name, if /// such a field exists in the underlying view. void read(const std::string_view& _name, const InputVarType& _var) const { assign_to_matching_field(*r_, _name, _var, view_, errors_, found_, set_, std::make_integer_sequence()); } /// Assigns the parsed version of _var to the field signified by _index, if /// such a field exists in the underlying view. /// Note that schemaful formats can assign by index. void read(const int _index, const InputVarType& _var) const { assign_to_matching_field(*r_, _index, _var, view_, errors_, found_, set_, std::make_integer_sequence()); } private: template static bool is_matching(const int _current_index) { return _current_index == i; } template static bool is_matching(const std::string_view& _current_name) { return _current_name == FieldType::name(); } template static void assign_if_field_matches(const R& _r, const auto _current_name_or_index, const auto& _var, auto* _view, auto* _errors, auto* _found, auto* _set, bool* _already_assigned) { using FieldType = tuple_element_t; using OriginalType = typename FieldType::Type; using T = std::remove_cvref_t>; constexpr auto name = FieldType::name(); if (!(*_already_assigned) && !std::get(*_found) && is_matching(_current_name_or_index)) { std::get(*_found) = true; *_already_assigned = true; auto res = Parser::read(_r, _var); if (!res) { std::stringstream stream; stream << "Failed to parse field '" << std::string(name) << "': " << res.error().what(); _errors->emplace_back(Error(stream.str())); return; } if constexpr (std::is_pointer_v) { move_to(rfl::get(*_view), &(*res)); } else { rfl::get(*_view) = std::move(*res); } std::get(*_set) = true; } } template static void assign_to_extra_fields(const R& _r, const std::string_view& _current_name, const auto& _var, auto* _view, auto* _errors, auto* _found, auto* _set) { auto* extra_fields = _view->template get<_pos>(); using ExtraFieldsType = std::remove_cvref_t>; using T = std::remove_cvref_t< std::remove_pointer_t>; if (!std::get<_pos>(*_set)) { ::new (extra_fields) ExtraFieldsType(); std::get<_pos>(*_set) = true; std::get<_pos>(*_found) = true; } auto res = Parser::read(_r, _var); if (!res) { std::stringstream stream; stream << "Failed to parse field '" << _current_name << "': " << res.error().what(); _errors->emplace_back(Error(stream.str())); return; } extra_fields->emplace(std::string(_current_name), std::move(*res)); } template static void assign_to_matching_field(const R& _r, const auto _current_name_or_index, const auto& _var, auto* _view, auto* _errors, auto* _found, auto* _set, std::integer_sequence) { bool already_assigned = false; (assign_if_field_matches(_r, _current_name_or_index, _var, _view, _errors, _found, _set, &already_assigned), ...); if constexpr (ViewType::pos_extra_fields() != -1) { static_assert(!schemaful::IsSchemafulReader, "rfl::ExtraFields are not supported for schemaful formats, " "because schemaful formats cannot have extra fields."); constexpr int pos = ViewType::pos_extra_fields(); if (!already_assigned) { assign_to_extra_fields(_r, _current_name_or_index, _var, _view, _errors, _found, _set); } } else if constexpr (ProcessorsType::no_extra_fields_) { static_assert( !schemaful::IsSchemafulReader, "Passing rfl::NoExtraFields to a schemaful format does not make " "sense, because schemaful formats cannot have extra fields."); if (!already_assigned) { std::stringstream stream; stream << "Value named '" << _current_name_or_index << "' not used. Remove the rfl::NoExtraFields processor or add " "rfl::ExtraFields to avoid this error message."; _errors->emplace_back(Error(stream.str())); } } } template static void move_to(Target* _t, Source* _s) { if constexpr (std::is_const_v) { return move_to(const_cast*>(_t), _s); } else if constexpr (!rfl::internal::is_array_v && !std::is_array_v) { ::new (_t) Target(std::move(*_s)); } else if constexpr (rfl::internal::is_array_v) { static_assert(std::is_array_v, "Expected target to be a c-array."); for (size_t i = 0; i < _s->arr_.size(); ++i) { move_to(&((*_t)[i]), &(_s->arr_[i])); } } else { for (size_t i = 0; i < _s->size(); ++i) { move_to(&((*_t)[i]), &((*_s)[i])); } } } private: /// The underlying reader. const R* r_; /// The underlying view. ViewType* view_; /// Indicates that a certain field has been found. std::array* found_; /// Indicates that a certain field has been successfully set - necessary, /// because we have to trigger the destructors manually. std::array* set_; /// Collects any errors we may have come across. std::vector* errors_; }; } // namespace rfl::parsing #endif