2024-05-31 22:59:00 -04:00
|
|
|
#ifndef RFL_PARSING_NAMEDTUPLEPARSER_HPP_
|
|
|
|
#define RFL_PARSING_NAMEDTUPLEPARSER_HPP_
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <map>
|
|
|
|
#include <tuple>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <unordered_map>
|
2024-06-05 19:04:53 -04:00
|
|
|
#include <utility>
|
2024-05-31 22:59:00 -04:00
|
|
|
|
|
|
|
#include "../NamedTuple.hpp"
|
|
|
|
#include "../Result.hpp"
|
|
|
|
#include "../always_false.hpp"
|
|
|
|
#include "../internal/Memoization.hpp"
|
|
|
|
#include "../internal/is_array.hpp"
|
|
|
|
#include "../internal/is_attribute.hpp"
|
|
|
|
#include "../internal/is_basic_type.hpp"
|
|
|
|
#include "../internal/is_skip.hpp"
|
|
|
|
#include "../internal/strings/replace_all.hpp"
|
|
|
|
#include "../to_view.hpp"
|
|
|
|
#include "AreReaderAndWriter.hpp"
|
|
|
|
#include "Parent.hpp"
|
|
|
|
#include "Parser_base.hpp"
|
|
|
|
#include "ViewReader.hpp"
|
|
|
|
#include "is_empty.hpp"
|
|
|
|
#include "is_required.hpp"
|
|
|
|
#include "schema/Type.hpp"
|
|
|
|
#include "to_single_error_message.hpp"
|
|
|
|
|
|
|
|
namespace rfl {
|
2024-06-08 14:10:59 -04:00
|
|
|
namespace parsing {
|
|
|
|
|
2024-06-08 15:53:06 -04:00
|
|
|
template <
|
|
|
|
class R,
|
|
|
|
class W,
|
|
|
|
bool _ignore_empty_containers,
|
|
|
|
bool _all_required,
|
|
|
|
class ProcessorsType,
|
|
|
|
class... FieldTypes>
|
2024-06-08 14:10:59 -04:00
|
|
|
requires AreReaderAndWriter<R, W, NamedTuple<FieldTypes...>>
|
|
|
|
struct NamedTupleParser {
|
|
|
|
using InputObjectType = typename R::InputObjectType;
|
|
|
|
using InputVarType = typename R::InputVarType;
|
|
|
|
|
|
|
|
using OutputObjectType = typename W::OutputObjectType;
|
|
|
|
using OutputVarType = typename W::OutputVarType;
|
|
|
|
|
|
|
|
using ParentType = Parent<W>;
|
|
|
|
|
|
|
|
using NamedTupleType = NamedTuple<FieldTypes...>;
|
|
|
|
|
|
|
|
static constexpr size_t size_ = NamedTupleType::size();
|
|
|
|
|
|
|
|
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.
|
2024-06-08 15:53:06 -04:00
|
|
|
static Result<NamedTuple<FieldTypes...>>
|
|
|
|
read(const R& _r, const InputVarType& _var) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
alignas(NamedTuple<FieldTypes...>) unsigned char
|
2024-06-08 15:53:06 -04:00
|
|
|
buf[sizeof(NamedTuple<FieldTypes...>)];
|
2024-06-08 14:10:59 -04:00
|
|
|
auto ptr = reinterpret_cast<NamedTuple<FieldTypes...>*>(buf);
|
|
|
|
auto view = rfl::to_view(*ptr);
|
|
|
|
using ViewType = std::remove_cvref_t<decltype(view)>;
|
|
|
|
const auto err =
|
|
|
|
Parser<R, W, ViewType, ProcessorsType>::read_view(_r, _var, &view);
|
|
|
|
if (err) [[unlikely]] { return *err; }
|
|
|
|
return *ptr;
|
2024-05-31 22:59:00 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
|
|
|
/// Reads the data into a view.
|
|
|
|
static std::optional<Error> read_view(
|
2024-06-08 15:53:06 -04:00
|
|
|
const R& _r,
|
|
|
|
const InputVarType& _var,
|
|
|
|
NamedTuple<FieldTypes...>* _view
|
|
|
|
) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
auto obj = _r.to_object(_var);
|
|
|
|
if (!obj) [[unlikely]] { return obj.error(); }
|
|
|
|
return read_object(_r, *obj, _view);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// For writing, we do not need to make the distinction between
|
|
|
|
/// default-constructible and non-default constructible fields.
|
|
|
|
template <class P>
|
2024-06-08 15:53:06 -04:00
|
|
|
static void write(
|
|
|
|
const W& _w,
|
|
|
|
const NamedTuple<FieldTypes...>& _tup,
|
|
|
|
const P& _parent
|
|
|
|
) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
auto obj = ParentType::add_object(_w, _tup.size(), _parent);
|
|
|
|
build_object_recursively(_w, _tup, &obj);
|
|
|
|
_w.end_object(&obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// For generating the schema, we also do not need to make the distinction
|
|
|
|
/// between default-constructible and non-default constructible fields.
|
|
|
|
template <size_t _i = 0>
|
|
|
|
static schema::Type to_schema(
|
|
|
|
std::map<std::string, schema::Type>* _definitions,
|
2024-06-08 15:53:06 -04:00
|
|
|
std::map<std::string, schema::Type> _values = {}
|
|
|
|
) {
|
2024-06-08 14:10:59 -04:00
|
|
|
using Type = schema::Type;
|
|
|
|
using T = NamedTuple<FieldTypes...>;
|
|
|
|
constexpr size_t size = T::size();
|
|
|
|
if constexpr (_i == size) {
|
|
|
|
return Type {Type::Object {_values}};
|
|
|
|
} else {
|
2024-06-08 15:53:06 -04:00
|
|
|
using F = std::tuple_element_t<
|
|
|
|
_i, typename NamedTuple<FieldTypes...>::Fields>;
|
2024-06-08 14:10:59 -04:00
|
|
|
using U = typename F::Type;
|
|
|
|
if constexpr (!internal::is_skip_v<U>) {
|
|
|
|
_values[std::string(F::name())] =
|
|
|
|
Parser<R, W, U, ProcessorsType>::to_schema(_definitions);
|
2024-05-31 22:59:00 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
return to_schema<_i + 1>(_definitions, _values);
|
2024-05-31 22:59:00 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <int _i = 0>
|
|
|
|
static void build_object_recursively(
|
2024-06-08 15:53:06 -04:00
|
|
|
const W& _w,
|
2024-06-08 14:10:59 -04:00
|
|
|
const NamedTuple<FieldTypes...>& _tup,
|
2024-06-08 15:53:06 -04:00
|
|
|
OutputObjectType* _ptr
|
|
|
|
) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
if constexpr (_i >= sizeof...(FieldTypes)) {
|
|
|
|
return;
|
2024-05-31 22:59:00 -04:00
|
|
|
} else {
|
2024-06-08 14:10:59 -04:00
|
|
|
using FieldType =
|
|
|
|
typename std::tuple_element<_i, std::tuple<FieldTypes...>>::type;
|
2024-06-08 15:53:06 -04:00
|
|
|
using ValueType = std::remove_cvref_t<typename FieldType::Type>;
|
|
|
|
const auto& value = rfl::get<_i>(_tup);
|
|
|
|
constexpr auto name = FieldType::name_.string_view();
|
|
|
|
const auto new_parent = typename ParentType::Object {name, _ptr};
|
2024-06-08 14:10:59 -04:00
|
|
|
if constexpr (!_all_required &&
|
|
|
|
!is_required<ValueType, _ignore_empty_containers>()) {
|
|
|
|
if (!is_empty(value)) {
|
|
|
|
if constexpr (internal::is_attribute_v<ValueType>) {
|
|
|
|
Parser<R, W, ValueType, ProcessorsType>::write(
|
2024-06-08 15:53:06 -04:00
|
|
|
_w, value, new_parent.as_attribute()
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
} else {
|
2024-06-08 15:53:06 -04:00
|
|
|
Parser<R, W, ValueType, ProcessorsType>::write(
|
|
|
|
_w, value, new_parent
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if constexpr (internal::is_attribute_v<ValueType>) {
|
|
|
|
Parser<R, W, ValueType, ProcessorsType>::write(
|
2024-06-08 15:53:06 -04:00
|
|
|
_w, value, new_parent.as_attribute()
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
} else {
|
2024-06-08 15:53:06 -04:00
|
|
|
Parser<R, W, ValueType, ProcessorsType>::write(
|
|
|
|
_w, value, new_parent
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return build_object_recursively<_i + 1>(_w, _tup, _ptr);
|
2024-05-31 22:59:00 -04:00
|
|
|
}
|
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
|
|
|
/// Generates error messages for when fields are missing.
|
|
|
|
template <int _i>
|
|
|
|
static void handle_one_missing_field(
|
|
|
|
const std::array<bool, size_>& _found,
|
2024-06-08 15:53:06 -04:00
|
|
|
const NamedTupleType& _view,
|
|
|
|
std::array<bool, size_>* _set,
|
|
|
|
std::vector<Error>* _errors
|
|
|
|
) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
using FieldType =
|
|
|
|
std::tuple_element_t<_i, typename NamedTupleType::Fields>;
|
|
|
|
using ValueType = std::remove_reference_t<
|
|
|
|
std::remove_pointer_t<typename FieldType::Type>>;
|
|
|
|
|
|
|
|
if (!std::get<_i>(_found)) {
|
|
|
|
if constexpr (_all_required ||
|
|
|
|
is_required<ValueType, _ignore_empty_containers>()) {
|
|
|
|
constexpr auto current_name =
|
2024-06-08 15:53:06 -04:00
|
|
|
std::tuple_element_t<_i, typename NamedTupleType::Fields>::name(
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
_errors->emplace_back(Error(
|
2024-06-08 15:53:06 -04:00
|
|
|
"Field named '" + std::string(current_name) + "' not found."
|
|
|
|
));
|
2024-06-08 14:10:59 -04:00
|
|
|
} else {
|
|
|
|
if constexpr (!std::is_const_v<ValueType>) {
|
|
|
|
::new (rfl::get<_i>(_view)) ValueType();
|
|
|
|
} else {
|
|
|
|
using NonConstT = std::remove_const_t<ValueType>;
|
|
|
|
::new (const_cast<NonConstT*>(rfl::get<_i>(_view))) NonConstT();
|
|
|
|
}
|
|
|
|
std::get<_i>(*_set) = true;
|
|
|
|
}
|
2024-05-31 22:59:00 -04:00
|
|
|
}
|
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
|
|
|
/// Generates error messages for when fields are missing.
|
|
|
|
template <int... _is>
|
2024-06-08 15:53:06 -04:00
|
|
|
static void
|
|
|
|
handle_missing_fields(const std::array<bool, size_>& _found, const NamedTupleType& _view, std::array<bool, size_>* _set, std::vector<Error>* _errors, std::integer_sequence<int, _is...>) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
(handle_one_missing_field<_is>(_found, _view, _set, _errors), ...);
|
|
|
|
}
|
|
|
|
|
2024-06-08 15:53:06 -04:00
|
|
|
static std::optional<Error> read_object(
|
|
|
|
const R& _r,
|
|
|
|
const InputObjectType& _obj,
|
|
|
|
NamedTupleType* _view
|
|
|
|
) noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
auto found = std::array<bool, NamedTupleType::size()>();
|
|
|
|
found.fill(false);
|
|
|
|
auto set = std::array<bool, NamedTupleType::size()>();
|
|
|
|
set.fill(false);
|
|
|
|
std::vector<Error> errors;
|
2024-06-08 15:53:06 -04:00
|
|
|
const auto object_reader =
|
|
|
|
ViewReader<R, W, NamedTupleType, ProcessorsType>(
|
|
|
|
&_r, _view, &found, &set, &errors
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
const auto err = _r.read_object(object_reader, _obj);
|
|
|
|
if (err) { return *err; }
|
2024-06-08 15:53:06 -04:00
|
|
|
handle_missing_fields(
|
|
|
|
found, *_view, &set, &errors,
|
|
|
|
std::make_integer_sequence<int, size_>()
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
if (errors.size() != 0) {
|
|
|
|
object_reader.call_destructors_where_necessary();
|
|
|
|
return to_single_error_message(errors);
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace parsing
|
|
|
|
} // namespace rfl
|
2024-05-31 22:59:00 -04:00
|
|
|
|
|
|
|
#endif
|