#ifndef RFL_PARSING_PARSER_VARIANT_HPP_ #define RFL_PARSING_PARSER_VARIANT_HPP_ #include #include #include #include #include "../NamedTuple.hpp" #include "../Ref.hpp" #include "../Result.hpp" #include "../Variant.hpp" #include "../always_false.hpp" #include "../internal/all_fields.hpp" #include "../internal/to_ptr_field.hpp" #include "FieldVariantParser.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "VariantAlternativeWrapper.hpp" #include "schema/Type.hpp" #include "schemaful/IsSchemafulReader.hpp" #include "schemaful/IsSchemafulWriter.hpp" #include "schemaful/VariantReader.hpp" #include "to_single_error_message.hpp" namespace rfl::parsing { template requires AreReaderAndWriter> class Parser, ProcessorsType> { template using ptr_field_t = decltype(internal::to_ptr_field(std::declval())); using ParentType = Parent; public: using InputVarType = typename R::InputVarType; static Result> read( const R& _r, const InputVarType& _var) noexcept { if constexpr (internal::all_fields>()) { if constexpr (schemaful::IsSchemafulReader) { using WrappedType = rfl::Variant...>; return Parser::read(_r, _var) .transform( [](auto&& _variant) -> std::variant { return std::move(_variant).visit([](auto&& _named_tuple) { return std::variant(std::move( std::move(_named_tuple).fields().template get<0>())); }); }); } else { const auto wrap = [](auto&& _v) { return std::variant(std::move(_v)); }; const auto to_std_variant = [&](auto&& _v) { return rfl::visit(wrap, std::move(_v)); }; return FieldVariantParser::read(_r, _var) .transform(to_std_variant); } } else if constexpr (schemaful::IsSchemafulReader) { using V = schemaful::VariantReader, ProcessorsType, AlternativeTypes...>; return _r.to_union(_var).and_then([&](const auto& _u) { return _r.template read_union, V>(_u); }); } else if constexpr (ProcessorsType::add_tags_to_variants_ || ProcessorsType::add_namespaced_tags_to_variants_) { constexpr bool remove_namespaces = ProcessorsType::add_tags_to_variants_; using FieldVariantType = rfl::Variant< VariantAlternativeWrapper...>; const auto from_field_variant = [](auto&& _field) -> std::variant { return std::move(_field.value()); }; return Parser::read(_r, _var) .transform([&](FieldVariantType&& _f) { return _f.visit(from_field_variant); }); } else { std::optional> result; std::vector errors; errors.reserve(sizeof...(AlternativeTypes)); read_variant( _r, _var, &result, &errors, std::make_integer_sequence()); if (result) { return std::move(*result); } else { return error( to_single_error_message(errors, "Could not parse the variant. Each of the " "possible alternatives failed " "for the following reasons: ", 100000)); } } } template static void write(const W& _w, const std::variant& _variant, const P& _parent) { if constexpr (internal::all_fields>()) { if constexpr (schemaful::IsSchemafulWriter) { using WrappedType = rfl::Variant< NamedTuple>...>; const auto to_wrapped = [](const auto& _variant) -> WrappedType { return std::visit( [](const auto& _field) -> WrappedType { return make_named_tuple(internal::to_ptr_field(_field)); }, _variant); }; Parser::write( _w, to_wrapped(_variant), _parent); } else { const auto wrap = [](const auto& _v) { return rfl::Variant...>( internal::to_ptr_field(_v)); }; const auto to_rfl_variant = [&](const auto& _v) { return std::visit(wrap, _v); }; FieldVariantParser< R, W, ProcessorsType, ptr_field_t...>::write(_w, to_rfl_variant(_variant), _parent); } } else if constexpr (schemaful::IsSchemafulWriter) { return std::visit( [&](const auto& _v) { using Type = std::remove_cvref_t; auto u = ParentType::add_union(_w, _parent); using UnionType = typename ParentType::template Union; auto p = UnionType{.index_ = static_cast(_variant.index()), .union_ = &u}; Parser::write(_w, _v, p); }, _variant); } else if constexpr (ProcessorsType::add_tags_to_variants_ || ProcessorsType::add_namespaced_tags_to_variants_) { constexpr bool remove_namespaces = ProcessorsType::add_tags_to_variants_; using FieldVariantType = rfl::Variant...>; const auto to_field_variant = [](const T& _t) -> FieldVariantType { return VariantAlternativeWrapper(&_t); }; Parser::write( _w, std::visit(to_field_variant, _variant), _parent); } else { const auto handle = [&](const auto& _v) { using Type = std::remove_cvref_t; Parser::write(_w, _v, _parent); }; return std::visit(handle, _variant); } } static schema::Type to_schema( std::map* _definitions) { if constexpr (internal::all_fields>()) { return FieldVariantParser::to_schema(_definitions); } else if constexpr (ProcessorsType::add_tags_to_variants_ || ProcessorsType::add_namespaced_tags_to_variants_) { constexpr bool remove_namespaces = ProcessorsType::add_tags_to_variants_; using FieldVariantType = rfl::Variant< VariantAlternativeWrapper...>; return Parser::to_schema( _definitions); } else { std::vector types; build_schema( _definitions, &types, std::make_integer_sequence()); return schema::Type{schema::Type::AnyOf{.types_ = std::move(types)}}; } } private: template static void add_to_schema(std::map* _definitions, std::vector* _types) noexcept { using U = std::remove_cvref_t< std::variant_alternative_t<_i, std::variant>>; _types->push_back(Parser::to_schema(_definitions)); } template static void build_schema(std::map* _definitions, std::vector* _types, std::integer_sequence) noexcept { (add_to_schema<_is>(_definitions, _types), ...); } template static void read_one_alternative( const R& _r, const InputVarType& _var, std::optional>* _result, std::vector* _errors) noexcept { if (!*_result) { using AltType = std::remove_cvref_t>; auto res = Parser::read(_r, _var); if (res) { _result->emplace(std::move(*res)); } else { _errors->emplace_back(std::move(res.error())); } } } template static void read_variant( const R& _r, const InputVarType& _var, std::optional>* _result, std::vector* _errors, std::integer_sequence) noexcept { (read_one_alternative<_is>(_r, _var, _result, _errors), ...); } }; } // namespace rfl::parsing #endif