#ifndef RFL_PARSING_TUPLEREADER_HPP_ #define RFL_PARSING_TUPLEREADER_HPP_ #include #include #include "../Result.hpp" #include "../Tuple.hpp" #include "../internal/is_array.hpp" #include "AreReaderAndWriter.hpp" #include "Parser_base.hpp" #include "is_required.hpp" namespace rfl::parsing { template class TupleReader { private: using InputVarType = typename R::InputVarType; static constexpr size_t size_ = rfl::tuple_size_v; public: TupleReader(const R* _r, TupleType* _tuple) : num_set_(0), r_(_r), tuple_(_tuple) {} ~TupleReader() = default; std::optional handle_missing_fields() const { std::optional err; if (num_set_ < size_) { handle_missing_fields_impl(&err); } return err; } size_t num_set() const { return num_set_; } std::optional read(const InputVarType& _var) const { std::optional err; read_impl(_var, &err); return err; } private: template void handle_missing_fields_impl(std::optional* _err) const noexcept { if constexpr (_i < size_) { if (num_set_ == _i) { using CurrentType = std::remove_cvref_t>; if constexpr (_all_required || is_required()) { *_err = Error("Field " + std::to_string(_i) + " was required, but missing."); return; } else { ::new (&(rfl::get<_i>(*tuple_))) CurrentType(); ++num_set_; } } handle_missing_fields_impl<_i + 1>(_err); } } template void read_impl(const InputVarType& _var, std::optional* _err) const { if constexpr (_i < size_) { if (num_set_ == _i) { using CurrentType = std::remove_cvref_t>; auto res = Parser::read(*r_, _var); if (res) { move_to(&(rfl::get<_i>(*tuple_)), &(*res)); ++num_set_; } else { std::stringstream stream; stream << "Failed to parse field " << _i << ": " << res.error().what(); *_err = Error(stream.str()); } return; } read_impl<_i + 1>(_var, _err); } else { std::stringstream stream; stream << "Expected " << size_ << " fields, but got at least one more."; *_err = Error(stream.str()); return; } } template void move_to(Target* _t, Source* _s) const { if constexpr (std::is_const_v) { return move_to(const_cast*>(_t), _s); } else if constexpr (!internal::is_array_v && !std::is_array_v) { ::new (_t) Target(std::move(*_s)); } else if constexpr (internal::is_array_v) { 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: /// Indicates the last field that was set. mutable size_t num_set_; /// The underlying reader. const R* r_; /// The underlying tuple. TupleType* tuple_; }; } // namespace rfl::parsing #endif