draconisplusplus/include/rfl/parsing/FieldVariantReader.hpp
2024-06-08 14:10:59 -04:00

85 lines
2.5 KiB
C++

#ifndef RFL_PARSING_FIELDVARIANTREADER_HPP_
#define RFL_PARSING_FIELDVARIANTREADER_HPP_
#include <array>
#include <optional>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>
#include "../Result.hpp"
#include "../internal/is_array.hpp"
namespace rfl::parsing {
template <class R, class W, class ProcessorsType, class... FieldTypes>
class FieldVariantReader {
private:
using InputVarType = typename R::InputVarType;
using FieldVariantType = std::variant<FieldTypes...>;
using ResultType = Result<FieldVariantType>;
public:
FieldVariantReader(const R* _r,
std::optional<Result<FieldVariantType>>* _field_variant)
: r_(_r), field_variant_(_field_variant) {}
~FieldVariantReader() = default;
template <size_t _i = 0>
void read(const std::string_view& _disc_value,
const InputVarType& _var) const noexcept {
if constexpr (_i < sizeof...(FieldTypes)) {
if constexpr (_i == 0) {
if (*field_variant_) {
*field_variant_ = Error(
"Could not parse: Expected the object to have "
"exactly one field, but found more than one.");
return;
}
}
using FieldType = std::remove_cvref_t<
typename std::tuple_element<_i, std::tuple<FieldTypes...>>::type>;
using ValueType = std::remove_cvref_t<typename FieldType::Type>;
constexpr auto key = FieldType::name_.string_view();
if (key == _disc_value) {
const auto to_variant = [](ValueType&& _val) {
return std::variant<FieldTypes...>(FieldType(std::move(_val)));
};
const auto embellish_error = [&](const Error& _e) {
return Error("Could not parse std::variant with field '" +
std::string(_disc_value) + "': " + _e.what());
};
*field_variant_ =
Parser<R, W, ValueType, ProcessorsType>::read(*r_, _var)
.transform(to_variant)
.or_else(embellish_error);
return;
} else {
read<_i + 1>(_disc_value, _var);
}
} else {
*field_variant_ = Error(
"Could not parse std::variant, could not match field named "
"'" +
std::string(_disc_value) + "'.");
}
}
private:
/// The underlying reader.
const R* r_;
/// The underlying field variant.
std::optional<Result<FieldVariantType>>* field_variant_;
};
} // namespace rfl::parsing
#endif