feat(reflect-cpp): Switched from glaze -> reflect cpp
A bug was discovered in glaze which prevented valid toml output. We have switched to toml++ and reflect-cpp. The interface has remained the same so this should not break any code
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
#ifndef RFL_PARSING_AREREADERANDWRITER_HPP_
|
||||
#define RFL_PARSING_AREREADERANDWRITER_HPP_
|
||||
|
||||
#include "IsReader.hpp"
|
||||
#include "IsWriter.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T>
|
||||
concept AreReaderAndWriter = IsReader<R, T> && IsWriter<W, T>;
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
86
build-config/reflect-cpp/include/rfl/parsing/ArrayReader.hpp
Normal file
86
build-config/reflect-cpp/include/rfl/parsing/ArrayReader.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef RFL_PARSING_ARRAYREADER_HPP_
|
||||
#define RFL_PARSING_ARRAYREADER_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType, class T, size_t _size>
|
||||
class ArrayReader {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
static constexpr size_t size_ = _size;
|
||||
|
||||
public:
|
||||
ArrayReader(const R* _r, std::array<T, _size>* _array)
|
||||
: array_(_array), num_set_(0), r_(_r) {}
|
||||
|
||||
~ArrayReader() = default;
|
||||
|
||||
std::optional<Error> check_size() const {
|
||||
if (num_set_ != size_) {
|
||||
return Error("Expected " + std::to_string(size_) + " elements, got " +
|
||||
std::to_string(num_set_) + ".");
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
size_t num_set() const { return num_set_; }
|
||||
|
||||
std::optional<Error> read(const InputVarType& _var) const {
|
||||
if (num_set_ == size_) {
|
||||
return Error("Expected " + std::to_string(size_) +
|
||||
" elements, got at least " + std::to_string(size_ + 1) +
|
||||
".");
|
||||
}
|
||||
auto res =
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(*r_, _var);
|
||||
if (res) {
|
||||
move_to(&((*array_)[num_set_]), &(*res));
|
||||
} else {
|
||||
return Error("Failed to parse element " + std::to_string(num_set_) +
|
||||
": " + res.error().what());
|
||||
}
|
||||
++num_set_;
|
||||
return std::optional<Error>();
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Target, class Source>
|
||||
void move_to(Target* _t, Source* _s) const {
|
||||
if constexpr (std::is_const_v<Target>) {
|
||||
return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
|
||||
} else if constexpr (!internal::is_array_v<Source> &&
|
||||
!std::is_array_v<Target>) {
|
||||
::new (_t) Target(std::move(*_s));
|
||||
} else if constexpr (internal::is_array_v<Source>) {
|
||||
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 array.
|
||||
std::array<T, size_>* array_;
|
||||
|
||||
/// Indicates the current field
|
||||
mutable size_t num_set_;
|
||||
|
||||
/// The underlying reader.
|
||||
const R* r_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef RFL_PARSING_CUSTOMPARSER_HPP_
|
||||
#define RFL_PARSING_CUSTOMPARSER_HPP_
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include "../Tuple.hpp"
|
||||
#include "../internal/has_to_class_method_v.hpp"
|
||||
#include "../internal/to_ptr_field_tuple.hpp"
|
||||
#include "Parser.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType, class OriginalClass,
|
||||
class HelperStruct>
|
||||
struct CustomParser {
|
||||
using CustomParserHelperStruct = std::remove_cvref_t<HelperStruct>;
|
||||
|
||||
static Result<OriginalClass> read(const R& _r, const auto& _var) noexcept {
|
||||
const auto to_class = [](auto&& _h) -> Result<OriginalClass> {
|
||||
try {
|
||||
if constexpr (internal::has_to_class_method_v<HelperStruct>) {
|
||||
return _h.to_class();
|
||||
} else {
|
||||
auto ptr_field_tuple = internal::to_ptr_field_tuple(_h);
|
||||
const auto class_from_ptrs = [](auto&... _ptrs) {
|
||||
return OriginalClass(std::move(*_ptrs.value_)...);
|
||||
};
|
||||
return rfl::apply(class_from_ptrs, ptr_field_tuple);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
};
|
||||
return Parser<R, W, CustomParserHelperStruct, ProcessorsType>::read(_r,
|
||||
_var)
|
||||
.and_then(to_class);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static auto write(const W& _w, const OriginalClass& _p, const P& _parent) {
|
||||
Parser<R, W, CustomParserHelperStruct, ProcessorsType>::write(
|
||||
_w, HelperStruct::from_class(_p), _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, CustomParserHelperStruct, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef RFL_PARSING_FIELD_VARIANT_PARSER_HPP_
|
||||
#define RFL_PARSING_FIELD_VARIANT_PARSER_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Tuple.hpp"
|
||||
#include "../Variant.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../visit.hpp"
|
||||
#include "FieldVariantReader.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
/// To be used when all options of the variants are rfl::Field. Essentially,
|
||||
/// this is an externally tagged union.
|
||||
template <class R, class W, class ProcessorsType, class... FieldTypes>
|
||||
requires AreReaderAndWriter<R, W, rfl::Variant<FieldTypes...>>
|
||||
struct FieldVariantParser {
|
||||
using FieldVariantType = rfl::Variant<FieldTypes...>;
|
||||
using ResultType = Result<FieldVariantType>;
|
||||
|
||||
public:
|
||||
using InputObjectType = typename R::InputObjectType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static ResultType read(const R& _r, const InputVarType& _var) noexcept {
|
||||
static_assert(
|
||||
internal::no_duplicate_field_names<rfl::Tuple<FieldTypes...>>(),
|
||||
"Externally tagged variants cannot have duplicate field "
|
||||
"names.");
|
||||
|
||||
const auto to_result = [&](const auto _obj) -> ResultType {
|
||||
auto field_variant = std::optional<Result<FieldVariantType>>();
|
||||
const auto reader =
|
||||
FieldVariantReader<R, W, ProcessorsType, FieldTypes...>(
|
||||
&_r, &field_variant);
|
||||
auto err = _r.read_object(reader, _obj);
|
||||
if (err) {
|
||||
return error(*err);
|
||||
}
|
||||
if (!field_variant) {
|
||||
return error(
|
||||
"Could not parse: Expected the object to have "
|
||||
"exactly one field, but found more than one.");
|
||||
}
|
||||
return std::move(*field_variant);
|
||||
};
|
||||
|
||||
return _r.to_object(_var).and_then(to_result);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const rfl::Variant<FieldTypes...>& _v,
|
||||
const P& _parent) {
|
||||
static_assert(
|
||||
internal::no_duplicate_field_names<rfl::Tuple<FieldTypes...>>(),
|
||||
"Externally tagged variants cannot have duplicate field "
|
||||
"names.");
|
||||
|
||||
_v.visit([&](const auto& _field) {
|
||||
const auto named_tuple = make_named_tuple(internal::to_ptr_field(_field));
|
||||
using NamedTupleType = std::remove_cvref_t<decltype(named_tuple)>;
|
||||
Parser<R, W, NamedTupleType, ProcessorsType>::write(_w, named_tuple,
|
||||
_parent);
|
||||
});
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions,
|
||||
[[maybe_unused]] std::vector<schema::Type> _types = {}) {
|
||||
using VariantType = rfl::Variant<NamedTuple<FieldTypes>...>;
|
||||
return Parser<R, W, VariantType, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef RFL_PARSING_FIELDVARIANTREADER_HPP_
|
||||
#define RFL_PARSING_FIELDVARIANTREADER_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Variant.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "../internal/nth_element_t.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType, class... FieldTypes>
|
||||
class FieldVariantReader {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using FieldVariantType = rfl::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;
|
||||
|
||||
void read(const std::string_view& _disc_value,
|
||||
const InputVarType& _var) const noexcept {
|
||||
try_matching_fields(
|
||||
_disc_value, _var,
|
||||
std::make_integer_sequence<int, sizeof...(FieldTypes)>());
|
||||
if (!*field_variant_) {
|
||||
std::stringstream stream;
|
||||
stream << "Could not parse rfl::Variant, could not match field named "
|
||||
"'"
|
||||
<< _disc_value << "'.";
|
||||
*field_variant_ = error(stream.str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <int... _is>
|
||||
void try_matching_fields(const std::string_view& _disc_value,
|
||||
const InputVarType& _var,
|
||||
std::integer_sequence<int, _is...>) const noexcept {
|
||||
(try_one<_is>(_disc_value, _var), ...);
|
||||
}
|
||||
|
||||
template <int _i>
|
||||
void try_one(const std::string_view& _disc_value,
|
||||
const InputVarType& _var) const noexcept {
|
||||
using FieldType =
|
||||
std::remove_cvref_t<internal::nth_element_t<_i, FieldTypes...>>;
|
||||
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 rfl::Variant<FieldTypes...>(FieldType(std::move(_val)));
|
||||
};
|
||||
const auto embellish_error = [&](const Error& _e) {
|
||||
std::stringstream stream;
|
||||
stream << "Could not parse rfl::Variant with field '"
|
||||
<< std::string(_disc_value) << "': " << _e.what();
|
||||
return Error(stream.str());
|
||||
};
|
||||
*field_variant_ = Parser<R, W, ValueType, ProcessorsType>::read(*r_, _var)
|
||||
.transform(to_variant)
|
||||
.transform_error(embellish_error);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// The underlying reader.
|
||||
const R* r_;
|
||||
|
||||
/// The underlying field variant.
|
||||
std::optional<Result<FieldVariantType>>* field_variant_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
91
build-config/reflect-cpp/include/rfl/parsing/IsReader.hpp
Normal file
91
build-config/reflect-cpp/include/rfl/parsing/IsReader.hpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef RFL_PARSING_ISREADER_HPP_
|
||||
#define RFL_PARSING_ISREADER_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../internal/is_basic_type.hpp"
|
||||
#include "../internal/wrap_in_rfl_array_t.hpp"
|
||||
#include "SupportsTaggedUnions.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R>
|
||||
struct MockArrayReader {
|
||||
std::optional<Error> read(typename R::InputVarType&) const {
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
struct MockObjectReader {
|
||||
void read(const std::string_view&, typename R::InputVarType&) const {}
|
||||
};
|
||||
|
||||
template <class R, class T>
|
||||
concept IsReader =
|
||||
(SupportsTaggedUnions<R> || schemaful::IsSchemafulReader<R>) &&
|
||||
requires(R r, std::string name,
|
||||
std::function<std::int16_t(std::string_view)> fct,
|
||||
MockArrayReader<R> array_reader, MockObjectReader<R> object_reader,
|
||||
typename R::InputArrayType arr, typename R::InputObjectType obj,
|
||||
typename R::InputVarType var, size_t idx) {
|
||||
/// Any Reader needs to define the following:
|
||||
///
|
||||
/// 1) An InputArrayType, which must be an array-like data structure.
|
||||
/// 2) An InputObjectType, which must contain key-value pairs.
|
||||
/// 3) An InputVarType, which must be able to represent either
|
||||
/// InputArrayType, InputObjectType or a basic type (bool, integral,
|
||||
/// floating point, std::string).
|
||||
/// 4) A static constexpr bool has_custom_constructor, that determines
|
||||
/// whether the class in question as a custom constructor, which might
|
||||
/// be called something like from_json_obj(...).
|
||||
|
||||
/// Determines whether a variable is empty (the NULL type).
|
||||
{ r.is_empty(var) } -> std::same_as<bool>;
|
||||
|
||||
/// Iterates through an array and writes the contained vars into
|
||||
/// an array reader.
|
||||
{ r.read_array(array_reader, arr) } -> std::same_as<std::optional<Error>>;
|
||||
|
||||
/// Iterates through an object and writes the key-value pairs into an
|
||||
/// object reader. This is what we use to handle structs and named tuples,
|
||||
/// making it a very important function.
|
||||
{
|
||||
r.read_object(object_reader, obj)
|
||||
} -> std::same_as<std::optional<Error>>;
|
||||
|
||||
/// Transforms var to a basic type (bool, integral,
|
||||
/// floating point, std::string)
|
||||
{
|
||||
r.template to_basic_type<internal::wrap_in_rfl_array_t<T>>(var)
|
||||
} -> std::same_as<rfl::Result<internal::wrap_in_rfl_array_t<T>>>;
|
||||
|
||||
/// Casts var as an InputArrayType.
|
||||
{
|
||||
r.to_array(var)
|
||||
} -> std::same_as<rfl::Result<typename R::InputArrayType>>;
|
||||
|
||||
/// Casts var as an InputObjectType.
|
||||
{
|
||||
r.to_object(var)
|
||||
} -> std::same_as<rfl::Result<typename R::InputObjectType>>;
|
||||
|
||||
/// Uses the custom constructor, if it has been determined that T has one
|
||||
/// (see above).
|
||||
{
|
||||
r.template use_custom_constructor<internal::wrap_in_rfl_array_t<T>>(var)
|
||||
} -> std::same_as<rfl::Result<internal::wrap_in_rfl_array_t<T>>>;
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
102
build-config/reflect-cpp/include/rfl/parsing/IsWriter.hpp
Normal file
102
build-config/reflect-cpp/include/rfl/parsing/IsWriter.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef RFL_PARSING_ISWRITER_HPP_
|
||||
#define RFL_PARSING_ISWRITER_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "../Result.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class W, class T>
|
||||
concept IsWriter = requires(W w, T t, std::string_view name,
|
||||
std::string basic_value,
|
||||
typename W::OutputArrayType arr,
|
||||
typename W::OutputObjectType obj,
|
||||
typename W::OutputVarType var, size_t size) {
|
||||
/// Sets an empty array as the root element of the document.
|
||||
/// Some serialization formats require you to pass the expected size in
|
||||
/// advance. If you are not working with such a format, you can ignore the
|
||||
/// parameter `size`. Returns the new array for further modification.
|
||||
{ w.array_as_root(size) } -> std::same_as<typename W::OutputArrayType>;
|
||||
|
||||
/// Sets an empty object as the root element of the document.
|
||||
/// Some serialization formats require you to pass the expected size in
|
||||
/// advance. If you are not working with such a format, you can ignore the
|
||||
/// parameter `size`.
|
||||
/// Returns the new object for further modification.
|
||||
{ w.object_as_root(size) } -> std::same_as<typename W::OutputObjectType>;
|
||||
|
||||
/// Sets a null as the root element of the document. Returns OutputVarType
|
||||
/// containing the null value.
|
||||
{ w.null_as_root() } -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Sets a basic value (bool, numeric, string) as the root element of the
|
||||
/// document. Returns an OutputVarType containing the new value.
|
||||
{ w.value_as_root(basic_value) } -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds an empty array to an existing array. Returns the new
|
||||
/// array for further modification.
|
||||
{
|
||||
w.add_array_to_array(size, &arr)
|
||||
} -> std::same_as<typename W::OutputArrayType>;
|
||||
|
||||
/// Adds an empty object to an existing array. Returns the new
|
||||
/// object for further modification.
|
||||
{
|
||||
w.add_object_to_array(size, &arr)
|
||||
} -> std::same_as<typename W::OutputObjectType>;
|
||||
|
||||
/// Adds an empty array to an existing object. The key or name of the field is
|
||||
/// signified by `name`. Returns the new array for further modification.
|
||||
{
|
||||
w.add_array_to_object(name, size, &obj)
|
||||
} -> std::same_as<typename W::OutputArrayType>;
|
||||
|
||||
/// Adds an empty object to an existing object. The key or name of the field
|
||||
/// is signified by `name`. Returns the new object for further modification.
|
||||
{
|
||||
w.add_object_to_object(name, size, &obj)
|
||||
} -> std::same_as<typename W::OutputObjectType>;
|
||||
|
||||
/// Adds a basic value (bool, numeric, string) to an array. Returns an
|
||||
/// OutputVarType containing the new value.
|
||||
{
|
||||
w.add_value_to_array(basic_value, &arr)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds a basic value (bool, numeric, string) to an existing object. The key
|
||||
/// or name of the field is signified by `name`. Returns an
|
||||
/// OutputVarType containing the new value.
|
||||
{
|
||||
w.add_value_to_object(name, basic_value, &obj)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds a null value to an array. Returns an
|
||||
/// OutputVarType containing the null value.
|
||||
{ w.add_null_to_array(&arr) } -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds a null value to an existing object. The key
|
||||
/// or name of the field is signified by `name`. Returns an
|
||||
/// OutputVarType containing the null value.
|
||||
{
|
||||
w.add_null_to_object(name, &obj)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Signifies to the writer that we do not want to add any further elements to
|
||||
/// this array. Some serialization formats require this. If you are working
|
||||
/// with a serialization format that doesn't, just leave the function empty.
|
||||
{ w.end_array(&arr) } -> std::same_as<void>;
|
||||
|
||||
/// Signifies to the writer that we do not want to add any further elements to
|
||||
/// this object. Some serialization formats require this. If you are working
|
||||
/// with a serialization format that doesn't, just leave the function empty.
|
||||
{ w.end_object(&obj) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
162
build-config/reflect-cpp/include/rfl/parsing/MapParser.hpp
Normal file
162
build-config/reflect-cpp/include/rfl/parsing/MapParser.hpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#ifndef RFL_PARSING_MAPPARSER_HPP_
|
||||
#define RFL_PARSING_MAPPARSER_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "MapReader.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "to_single_error_message.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class MapType, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, MapType>
|
||||
struct MapParser {
|
||||
public:
|
||||
using InputObjectType = typename R::InputObjectType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using KeyType = std::remove_cvref_t<typename MapType::value_type::first_type>;
|
||||
using ValueType =
|
||||
std::remove_cvref_t<typename MapType::value_type::second_type>;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<MapType> read(const R& _r, const InputVarType& _var) noexcept {
|
||||
const auto to_map = [&](auto obj) -> Result<MapType> {
|
||||
return make_map(_r, obj);
|
||||
};
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
return _r.to_map(_var).and_then(to_map);
|
||||
} else {
|
||||
return _r.to_object(_var).and_then(to_map);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const MapType& _m, const P& _parent) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
write_map(_w, _m, _parent);
|
||||
} else {
|
||||
write_object(_w, _m, _parent);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return schema::Type{schema::Type::StringMap{Ref<schema::Type>::make(
|
||||
Parser<R, W, ValueType, ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
|
||||
private:
|
||||
static Result<MapType> make_map(const R& _r, const auto& _obj_or_map) {
|
||||
MapType map;
|
||||
std::vector<Error> errors;
|
||||
const auto map_reader =
|
||||
MapReader<R, W, MapType, ProcessorsType>(&_r, &map, &errors);
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
const auto err = _r.read_map(map_reader, _obj_or_map);
|
||||
if (err) {
|
||||
return error(*err);
|
||||
}
|
||||
} else {
|
||||
const auto err = _r.read_object(map_reader, _obj_or_map);
|
||||
if (err) {
|
||||
return error(*err);
|
||||
}
|
||||
}
|
||||
if (errors.size() != 0) {
|
||||
return error(to_single_error_message(errors));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write_map(const W& _w, const MapType& _m, const P& _parent) {
|
||||
auto m = ParentType::add_map(_w, _m.size(), _parent);
|
||||
|
||||
using ParentMapType =
|
||||
typename ParentType::template Map<typename W::OutputMapType>;
|
||||
|
||||
for (const auto& [k, v] : _m) {
|
||||
if constexpr (internal::has_reflection_type_v<KeyType>) {
|
||||
using ReflT = typename KeyType::ReflectionType;
|
||||
|
||||
if constexpr (std::is_integral_v<ReflT> ||
|
||||
std::is_floating_point_v<ReflT>) {
|
||||
const auto name = std::to_string(k.reflection());
|
||||
const auto new_parent = ParentMapType{name, &m};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
} else {
|
||||
const auto name = k.reflection();
|
||||
const auto new_parent = ParentMapType{name, &m};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_integral_v<KeyType> ||
|
||||
std::is_floating_point_v<KeyType>) {
|
||||
const auto name = std::to_string(k);
|
||||
const auto new_parent = ParentMapType{name, &m};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
} else {
|
||||
const auto new_parent = ParentMapType{k, &m};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
}
|
||||
}
|
||||
_w.end_map(&m);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write_object(const W& _w, const MapType& _m, const P& _parent) {
|
||||
auto obj = ParentType::add_object(_w, _m.size(), _parent);
|
||||
for (const auto& [k, v] : _m) {
|
||||
if constexpr (internal::has_reflection_type_v<KeyType>) {
|
||||
using ReflT = typename KeyType::ReflectionType;
|
||||
|
||||
if constexpr (std::is_integral_v<ReflT> ||
|
||||
std::is_floating_point_v<ReflT>) {
|
||||
const auto name = std::to_string(k.reflection());
|
||||
const auto new_parent = typename ParentType::Object{name, &obj};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
} else {
|
||||
const auto name = k.reflection();
|
||||
const auto new_parent = typename ParentType::Object{name, &obj};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_integral_v<KeyType> ||
|
||||
std::is_floating_point_v<KeyType>) {
|
||||
const auto name = std::to_string(k);
|
||||
const auto new_parent = typename ParentType::Object{name, &obj};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
} else {
|
||||
const auto new_parent = typename ParentType::Object{k, &obj};
|
||||
Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
}
|
||||
}
|
||||
_w.end_object(&obj);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
117
build-config/reflect-cpp/include/rfl/parsing/MapReader.hpp
Normal file
117
build-config/reflect-cpp/include/rfl/parsing/MapReader.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef RFL_PARSING_MAPREADER_HPP_
|
||||
#define RFL_PARSING_MAPREADER_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/has_reflection_type_v.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class MapType, class ProcessorsType>
|
||||
class MapReader {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using KeyType = std::remove_cvref_t<typename MapType::value_type::first_type>;
|
||||
using ValueType =
|
||||
std::remove_cvref_t<typename MapType::value_type::second_type>;
|
||||
|
||||
public:
|
||||
MapReader(const R* _r, MapType* _map, std::vector<Error>* _errors)
|
||||
: r_(_r), map_(_map), errors_(_errors) {}
|
||||
|
||||
~MapReader() = default;
|
||||
|
||||
void read(const std::string_view& _name,
|
||||
const InputVarType& _var) const noexcept {
|
||||
auto res = get_pair(_name, _var);
|
||||
if (res) {
|
||||
map_->emplace(std::move(*res));
|
||||
} else {
|
||||
errors_->push_back(Error("Failed to parse field '" + std::string(_name) +
|
||||
"': " + res.error().what()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
Result<T> key_to_numeric(auto& _pair) const noexcept {
|
||||
try {
|
||||
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
|
||||
return static_cast<T>(std::stoll(_pair.first));
|
||||
} else if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>) {
|
||||
return static_cast<T>(std::stoull(_pair.first));
|
||||
} else if constexpr (std::is_floating_point_v<T>) {
|
||||
return static_cast<T>(std::stod(_pair.first));
|
||||
} else {
|
||||
static_assert(always_false_v<T>, "Unsupported type");
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::pair<KeyType, ValueType>> make_key(auto&& _pair) const noexcept {
|
||||
static_assert(std::is_rvalue_reference_v<decltype(_pair)>,
|
||||
"Expected an rvalue");
|
||||
const auto to_pair =
|
||||
[&](auto&& _key) -> Result<std::pair<KeyType, ValueType>> {
|
||||
try {
|
||||
using K = decltype(_key);
|
||||
return std::make_pair(KeyType(std::forward<K>(_key)),
|
||||
std::move(_pair.second));
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
};
|
||||
|
||||
if constexpr (std::is_integral_v<KeyType> ||
|
||||
std::is_floating_point_v<KeyType>) {
|
||||
return key_to_numeric<KeyType>(_pair).and_then(to_pair);
|
||||
|
||||
} else if constexpr (internal::has_reflection_type_v<KeyType>) {
|
||||
using ReflT = typename KeyType::ReflectionType;
|
||||
|
||||
if constexpr (std::is_integral_v<ReflT> ||
|
||||
std::is_floating_point_v<ReflT>) {
|
||||
return key_to_numeric<ReflT>(_pair).and_then(to_pair);
|
||||
} else {
|
||||
return to_pair(std::move(_pair.first));
|
||||
}
|
||||
|
||||
} else {
|
||||
return std::move(_pair);
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::pair<KeyType, ValueType>> get_pair(
|
||||
const std::string_view& _name, const InputVarType& _var) const noexcept {
|
||||
const auto to_pair = [&](ValueType&& _val) {
|
||||
auto pair = std::make_pair(std::string(_name), std::move(_val));
|
||||
return make_key(std::move(pair));
|
||||
};
|
||||
return Parser<R, W, std::remove_cvref_t<ValueType>, ProcessorsType>::read(
|
||||
*r_, _var)
|
||||
.and_then(to_pair);
|
||||
}
|
||||
|
||||
private:
|
||||
/// The underlying reader.
|
||||
const R* r_;
|
||||
|
||||
/// The underlying map.
|
||||
MapType* map_;
|
||||
|
||||
/// Collects any errors we may have come across.
|
||||
std::vector<Error>* errors_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,361 @@
|
||||
#ifndef RFL_PARSING_NAMEDTUPLEPARSER_HPP_
|
||||
#define RFL_PARSING_NAMEDTUPLEPARSER_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../NamedTuple.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/has_default_val_v.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "../internal/is_attribute.hpp"
|
||||
#include "../internal/is_basic_type.hpp"
|
||||
#include "../internal/is_default_val_v.hpp"
|
||||
#include "../internal/is_extra_fields.hpp"
|
||||
#include "../internal/is_skip.hpp"
|
||||
#include "../internal/no_duplicate_field_names.hpp"
|
||||
#include "../internal/nth_element_t.hpp"
|
||||
#include "../internal/ptr_cast.hpp"
|
||||
#include "../to_view.hpp"
|
||||
#include "AreReaderAndWriter.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "ViewReader.hpp"
|
||||
#include "ViewReaderWithDefault.hpp"
|
||||
#include "ViewReaderWithDefaultAndStrippedFieldNames.hpp"
|
||||
#include "ViewReaderWithStrippedFieldNames.hpp"
|
||||
#include "call_destructors_where_necessary.hpp"
|
||||
#include "is_empty.hpp"
|
||||
#include "is_required.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "to_single_error_message.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, bool _ignore_empty_containers, bool _all_required,
|
||||
bool _no_field_names, class ProcessorsType, class... FieldTypes>
|
||||
requires AreReaderAndWriter<R, W, NamedTuple<FieldTypes...>>
|
||||
struct NamedTupleParser {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
using NamedTupleType = NamedTuple<FieldTypes...>;
|
||||
|
||||
using ViewReaderType = std::conditional_t<
|
||||
_no_field_names,
|
||||
ViewReaderWithStrippedFieldNames<R, W, NamedTupleType, ProcessorsType>,
|
||||
ViewReader<R, W, NamedTupleType, ProcessorsType>>;
|
||||
|
||||
using ViewReaderWithDefaultType = std::conditional_t<
|
||||
_no_field_names,
|
||||
ViewReaderWithDefaultAndStrippedFieldNames<R, W, NamedTupleType,
|
||||
ProcessorsType>,
|
||||
ViewReaderWithDefault<R, W, NamedTupleType, ProcessorsType>>;
|
||||
|
||||
using InputObjectOrArrayType =
|
||||
std::conditional_t<_no_field_names, typename R::InputArrayType,
|
||||
typename R::InputObjectType>;
|
||||
using OutputObjectOrArrayType =
|
||||
std::conditional_t<_no_field_names, typename W::OutputArrayType,
|
||||
typename W::OutputObjectType>;
|
||||
|
||||
using SchemaType = std::conditional_t<_no_field_names, schema::Type::Tuple,
|
||||
schema::Type::Object>;
|
||||
|
||||
static constexpr size_t size_ = NamedTupleType::size();
|
||||
|
||||
static_assert(NamedTupleType::pos_extra_fields() == -1 || !_no_field_names,
|
||||
"You cannot use the rfl::NoFieldNames processor if you are "
|
||||
"including rfl::ExtraFields.");
|
||||
|
||||
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.
|
||||
static Result<NamedTuple<FieldTypes...>> read(
|
||||
const R& _r, const InputVarType& _var) noexcept {
|
||||
static_assert(
|
||||
internal::no_duplicate_field_names<typename NamedTupleType::Fields>());
|
||||
alignas(NamedTuple<FieldTypes...>) unsigned char
|
||||
buf[sizeof(NamedTuple<FieldTypes...>)];
|
||||
auto ptr = internal::ptr_cast<NamedTuple<FieldTypes...>*>(&buf);
|
||||
auto view = rfl::to_view(*ptr);
|
||||
using ViewType = std::remove_cvref_t<decltype(view)>;
|
||||
const auto [set, err] =
|
||||
Parser<R, W, ViewType, ProcessorsType>::read_view(_r, _var, &view);
|
||||
if (err) [[unlikely]] {
|
||||
call_destructors_where_necessary(set, &view);
|
||||
return error(*err);
|
||||
}
|
||||
auto res = Result<NamedTuple<FieldTypes...>>(std::move(*ptr));
|
||||
call_destructors_where_necessary(set, &view);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Reads the data into a view assuming no default values.
|
||||
static std::pair<std::array<bool, NamedTupleType::size()>,
|
||||
std::optional<Error>>
|
||||
read_view(const R& _r, const InputVarType& _var,
|
||||
NamedTuple<FieldTypes...>* _view) noexcept {
|
||||
static_assert(
|
||||
internal::no_duplicate_field_names<typename NamedTupleType::Fields>());
|
||||
if constexpr (_no_field_names) {
|
||||
auto arr = _r.to_array(_var);
|
||||
if (!arr) [[unlikely]] {
|
||||
auto set = std::array<bool, NamedTupleType::size()>{};
|
||||
return std::make_pair(set, arr.error());
|
||||
}
|
||||
return read_object_or_array(_r, *arr, _view);
|
||||
} else {
|
||||
auto obj = _r.to_object(_var);
|
||||
if (!obj) [[unlikely]] {
|
||||
auto set = std::array<bool, NamedTupleType::size()>{};
|
||||
return std::make_pair(set, obj.error());
|
||||
}
|
||||
return read_object_or_array(_r, *obj, _view);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the data into a view assuming default values.
|
||||
static std::optional<Error> read_view_with_default(
|
||||
const R& _r, const InputVarType& _var,
|
||||
NamedTuple<FieldTypes...>* _view) noexcept {
|
||||
static_assert(
|
||||
internal::no_duplicate_field_names<typename NamedTupleType::Fields>());
|
||||
if constexpr (_no_field_names) {
|
||||
auto arr = _r.to_array(_var);
|
||||
if (!arr) [[unlikely]] {
|
||||
return arr.error();
|
||||
}
|
||||
return read_object_or_array_with_default(_r, *arr, _view);
|
||||
} else {
|
||||
auto obj = _r.to_object(_var);
|
||||
if (!obj) [[unlikely]] {
|
||||
return obj.error();
|
||||
}
|
||||
return read_object_or_array_with_default(_r, *obj, _view);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const NamedTuple<FieldTypes...>& _tup,
|
||||
const P& _parent) {
|
||||
if constexpr (_no_field_names) {
|
||||
auto arr = ParentType::add_array(_w, _tup.num_fields(), _parent);
|
||||
build_object(_w, _tup, &arr, std::make_integer_sequence<int, size_>());
|
||||
_w.end_array(&arr);
|
||||
} else {
|
||||
auto obj = ParentType::add_object(_w, _tup.num_fields(), _parent);
|
||||
build_object(_w, _tup, &obj, std::make_integer_sequence<int, size_>());
|
||||
_w.end_object(&obj);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) noexcept {
|
||||
SchemaType schema;
|
||||
build_schema(_definitions, &schema,
|
||||
std::make_integer_sequence<int, size_>());
|
||||
return schema::Type{schema};
|
||||
}
|
||||
|
||||
private:
|
||||
template <int _i>
|
||||
static void add_field_to_object(const W& _w,
|
||||
const NamedTuple<FieldTypes...>& _tup,
|
||||
OutputObjectOrArrayType* _ptr) {
|
||||
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
|
||||
using ValueType = std::remove_cvref_t<typename FieldType::Type>;
|
||||
const auto value = rfl::get<_i>(_tup);
|
||||
if constexpr (internal::is_extra_fields_v<ValueType>) {
|
||||
for (const auto& [k, v] : *value) {
|
||||
const auto new_parent = make_parent(k, _ptr);
|
||||
Parser<R, W, std::remove_cvref_t<decltype(v)>, ProcessorsType>::write(
|
||||
_w, v, new_parent);
|
||||
}
|
||||
} else if constexpr (!_all_required && !_no_field_names &&
|
||||
!is_required<ValueType, _ignore_empty_containers>()) {
|
||||
constexpr auto name = FieldType::name_.string_view();
|
||||
const auto new_parent = make_parent(name, _ptr);
|
||||
if (!is_empty(value)) {
|
||||
if constexpr (internal::is_attribute_v<ValueType>) {
|
||||
Parser<R, W, ValueType, ProcessorsType>::write(
|
||||
_w, value, new_parent.as_attribute());
|
||||
} else {
|
||||
Parser<R, W, ValueType, ProcessorsType>::write(_w, value, new_parent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
constexpr auto name = FieldType::name_.string_view();
|
||||
const auto new_parent = make_parent(name, _ptr);
|
||||
if constexpr (internal::is_attribute_v<ValueType>) {
|
||||
Parser<R, W, ValueType, ProcessorsType>::write(
|
||||
_w, value, new_parent.as_attribute());
|
||||
} else {
|
||||
Parser<R, W, ValueType, ProcessorsType>::write(_w, value, new_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t _i>
|
||||
static void add_field_to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions,
|
||||
SchemaType* _schema) noexcept {
|
||||
using F = internal::nth_element_t<_i, FieldTypes...>;
|
||||
using U = std::remove_cvref_t<typename F::Type>;
|
||||
if constexpr (!internal::is_skip_v<U> && !internal::is_extra_fields_v<U>) {
|
||||
auto s = Parser<R, W, U, ProcessorsType>::to_schema(_definitions);
|
||||
if constexpr (_no_field_names) {
|
||||
_schema->types_.emplace_back(std::move(s));
|
||||
} else {
|
||||
_schema->types_[std::string(F::name())] = std::move(s);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <int... _is>
|
||||
static void build_object(const W& _w, const NamedTuple<FieldTypes...>& _tup,
|
||||
OutputObjectOrArrayType* _ptr,
|
||||
std::integer_sequence<int, _is...>) {
|
||||
(add_field_to_object<_is>(_w, _tup, _ptr), ...);
|
||||
}
|
||||
|
||||
template <int... _is>
|
||||
static void build_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
SchemaType* _schema,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
(add_field_to_schema<_is>(_definitions, _schema), ...);
|
||||
|
||||
if constexpr (NamedTupleType::pos_extra_fields() != -1) {
|
||||
using F = internal::nth_element_t<NamedTupleType::pos_extra_fields(),
|
||||
FieldTypes...>;
|
||||
using ExtraFieldsType = std::remove_cvref_t<typename F::Type>;
|
||||
using U = std::remove_cvref_t<typename ExtraFieldsType::Type>;
|
||||
_schema->additional_properties_ = std::make_shared<schema::Type>(
|
||||
Parser<R, W, U, ProcessorsType>::to_schema(_definitions));
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates error messages for when fields are missing.
|
||||
template <int _i>
|
||||
static void handle_one_missing_field(const std::array<bool, size_>& _found,
|
||||
const NamedTupleType& _view,
|
||||
std::array<bool, size_>* _set,
|
||||
std::vector<Error>* _errors) noexcept {
|
||||
using FieldType = internal::nth_element_t<_i, FieldTypes...>;
|
||||
using ValueType = std::remove_reference_t<
|
||||
std::remove_pointer_t<typename FieldType::Type>>;
|
||||
|
||||
if (!std::get<_i>(_found)) {
|
||||
constexpr bool is_required_field =
|
||||
!internal::is_default_val_v<ValueType> &&
|
||||
!internal::is_extra_fields_v<ValueType> &&
|
||||
(_all_required || is_required<ValueType, _ignore_empty_containers>());
|
||||
|
||||
if constexpr (is_required_field) {
|
||||
constexpr auto current_name =
|
||||
internal::nth_element_t<_i, FieldTypes...>::name();
|
||||
std::stringstream stream;
|
||||
stream << "Field named '" << std::string(current_name)
|
||||
<< "' not found.";
|
||||
_errors->emplace_back(Error(stream.str()));
|
||||
|
||||
} else if constexpr (!internal::has_default_val_v<NamedTupleType>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates error messages for when fields are missing.
|
||||
template <int... _is>
|
||||
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 {
|
||||
(handle_one_missing_field<_is>(_found, _view, _set, _errors), ...);
|
||||
}
|
||||
|
||||
static auto make_parent(const std::string_view& _name,
|
||||
OutputObjectOrArrayType* _ptr) {
|
||||
if constexpr (_no_field_names) {
|
||||
return typename ParentType::Array{_ptr};
|
||||
} else {
|
||||
return typename ParentType::Object{_name, _ptr};
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<std::array<bool, NamedTupleType::size()>,
|
||||
std::optional<Error>>
|
||||
read_object_or_array(const R& _r, const InputObjectOrArrayType& _obj_or_arr,
|
||||
NamedTupleType* _view) noexcept {
|
||||
auto found = std::array<bool, NamedTupleType::size()>();
|
||||
found.fill(false);
|
||||
auto set = std::array<bool, NamedTupleType::size()>();
|
||||
set.fill(false);
|
||||
std::vector<Error> errors;
|
||||
const auto reader = ViewReaderType(&_r, _view, &found, &set, &errors);
|
||||
if constexpr (_no_field_names) {
|
||||
const auto err = _r.read_array(reader, _obj_or_arr);
|
||||
if (err) {
|
||||
return std::make_pair(set, err);
|
||||
}
|
||||
} else {
|
||||
const auto err = _r.read_object(reader, _obj_or_arr);
|
||||
if (err) {
|
||||
return std::make_pair(set, err);
|
||||
}
|
||||
}
|
||||
handle_missing_fields(found, *_view, &set, &errors,
|
||||
std::make_integer_sequence<int, size_>());
|
||||
if (errors.size() != 0) {
|
||||
return std::make_pair(set, to_single_error_message(errors));
|
||||
}
|
||||
return std::make_pair(set, std::optional<Error>());
|
||||
}
|
||||
|
||||
static std::optional<Error> read_object_or_array_with_default(
|
||||
const R& _r, const InputObjectOrArrayType& _obj_or_arr,
|
||||
NamedTupleType* _view) noexcept {
|
||||
std::vector<Error> errors;
|
||||
const auto reader = ViewReaderWithDefaultType(&_r, _view, &errors);
|
||||
if constexpr (_no_field_names) {
|
||||
const auto err = _r.read_array(reader, _obj_or_arr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
const auto err = _r.read_object(reader, _obj_or_arr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if constexpr (internal::has_default_val_v<NamedTupleType> &&
|
||||
!ProcessorsType::default_if_missing_) {
|
||||
handle_missing_fields(reader.found(), *_view, nullptr, &errors,
|
||||
std::make_integer_sequence<int, size_>());
|
||||
}
|
||||
if (errors.size() != 0) {
|
||||
return to_single_error_message(errors);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
221
build-config/reflect-cpp/include/rfl/parsing/Parent.hpp
Normal file
221
build-config/reflect-cpp/include/rfl/parsing/Parent.hpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#ifndef RFL_PARSING_PARENT_HPP_
|
||||
#define RFL_PARSING_PARENT_HPP_
|
||||
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../always_false.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "supports_attributes.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class W>
|
||||
struct Parent {
|
||||
using OutputArrayType = typename W::OutputArrayType;
|
||||
using OutputObjectType = typename W::OutputObjectType;
|
||||
using OutputVarType = typename W::OutputVarType;
|
||||
|
||||
struct Array {
|
||||
OutputArrayType* arr_;
|
||||
};
|
||||
|
||||
// For schemaful formats only.
|
||||
template <class T>
|
||||
struct Map {
|
||||
std::string_view name_;
|
||||
T* map_;
|
||||
};
|
||||
|
||||
struct Object {
|
||||
std::string_view name_;
|
||||
OutputObjectType* obj_;
|
||||
bool is_attribute_ = false;
|
||||
Object as_attribute() const { return Object{name_, obj_, true}; }
|
||||
};
|
||||
|
||||
// For schemaful formats only.
|
||||
template <class T>
|
||||
struct Union {
|
||||
size_t index_;
|
||||
T* union_;
|
||||
};
|
||||
|
||||
struct Root {};
|
||||
|
||||
template <class ParentType>
|
||||
static OutputArrayType add_array(const W& _w, const size_t _size,
|
||||
const ParentType& _parent) {
|
||||
using Type = std::remove_cvref_t<ParentType>;
|
||||
if constexpr (std::is_same<Type, Array>()) {
|
||||
return _w.add_array_to_array(_size, _parent.arr_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Object>()) {
|
||||
return _w.add_array_to_object(_parent.name_, _size, _parent.obj_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Root>()) {
|
||||
return _w.array_as_root(_size);
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
if constexpr (std::is_same<Type, Map<typename W::OutputMapType>>()) {
|
||||
return _w.add_array_to_map(_parent.name_, _size, _parent.map_);
|
||||
} else if constexpr (std::is_same<Type,
|
||||
Union<typename W::OutputUnionType>>()) {
|
||||
return _w.add_array_to_union(_parent.index_, _size, _parent.union_);
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
}
|
||||
|
||||
// For schemaful formats only.
|
||||
template <class ParentType>
|
||||
static auto add_map(const W& _w, const size_t _size,
|
||||
const ParentType& _parent) {
|
||||
using Type = std::remove_cvref_t<ParentType>;
|
||||
if constexpr (std::is_same<Type, Array>()) {
|
||||
return _w.add_map_to_array(_size, _parent.arr_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Map<typename W::OutputMapType>>()) {
|
||||
return _w.add_map_to_map(_parent.name_, _size, _parent.map_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Object>()) {
|
||||
return _w.add_map_to_object(_parent.name_, _size, _parent.obj_);
|
||||
|
||||
} else if constexpr (std::is_same<Type,
|
||||
Union<typename W::OutputUnionType>>()) {
|
||||
return _w.add_map_to_union(_parent.index_, _size, _parent.union_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Root>()) {
|
||||
return _w.map_as_root(_size);
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParentType>
|
||||
static OutputObjectType add_object(const W& _w, const size_t _size,
|
||||
const ParentType& _parent) {
|
||||
using Type = std::remove_cvref_t<ParentType>;
|
||||
if constexpr (std::is_same<Type, Array>()) {
|
||||
return _w.add_object_to_array(_size, _parent.arr_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Object>()) {
|
||||
return _w.add_object_to_object(_parent.name_, _size, _parent.obj_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Root>()) {
|
||||
return _w.object_as_root(_size);
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
if constexpr (std::is_same<Type, Map<typename W::OutputMapType>>()) {
|
||||
return _w.add_object_to_map(_parent.name_, _size, _parent.map_);
|
||||
} else if constexpr (std::is_same<Type,
|
||||
Union<typename W::OutputUnionType>>()) {
|
||||
return _w.add_object_to_union(_parent.index_, _size, _parent.union_);
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParentType>
|
||||
static OutputVarType add_null(const W& _w, const ParentType& _parent) {
|
||||
using Type = std::remove_cvref_t<ParentType>;
|
||||
if constexpr (std::is_same<Type, Array>()) {
|
||||
return _w.add_null_to_array(_parent.arr_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Object>()) {
|
||||
if constexpr (supports_attributes<std::remove_cvref_t<W>>) {
|
||||
return _w.add_null_to_object(_parent.name_, _parent.obj_,
|
||||
_parent.is_attribute_);
|
||||
} else {
|
||||
return _w.add_null_to_object(_parent.name_, _parent.obj_);
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_same<Type, Root>()) {
|
||||
return _w.null_as_root();
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
if constexpr (std::is_same<Type, Map<typename W::OutputMapType>>()) {
|
||||
return _w.add_null_to_map(_parent.name_, _parent.map_);
|
||||
} else if constexpr (std::is_same<Type,
|
||||
Union<typename W::OutputUnionType>>()) {
|
||||
return _w.add_null_to_union(_parent.index_, _parent.union_);
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
}
|
||||
|
||||
// For schemaful formats only.
|
||||
template <class ParentType>
|
||||
static auto add_union(const W& _w, const ParentType& _parent) {
|
||||
using Type = std::remove_cvref_t<ParentType>;
|
||||
if constexpr (std::is_same<Type, Array>()) {
|
||||
return _w.add_union_to_array(_parent.arr_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Map<typename W::OutputMapType>>()) {
|
||||
return _w.add_union_to_map(_parent.name_, _parent.map_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Object>()) {
|
||||
return _w.add_union_to_object(_parent.name_, _parent.obj_);
|
||||
|
||||
} else if constexpr (std::is_same<Type,
|
||||
Union<typename W::OutputUnionType>>()) {
|
||||
return _w.add_union_to_union(_parent.index_, _parent.union_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Root>()) {
|
||||
return _w.union_as_root();
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
}
|
||||
|
||||
template <class ParentType, class T>
|
||||
static OutputVarType add_value(const W& _w, const T& _var,
|
||||
const ParentType& _parent) {
|
||||
using Type = std::remove_cvref_t<ParentType>;
|
||||
if constexpr (std::is_same<Type, Array>()) {
|
||||
return _w.add_value_to_array(_var, _parent.arr_);
|
||||
|
||||
} else if constexpr (std::is_same<Type, Object>()) {
|
||||
if constexpr (supports_attributes<std::remove_cvref_t<W>>) {
|
||||
return _w.add_value_to_object(_parent.name_, _var, _parent.obj_,
|
||||
_parent.is_attribute_);
|
||||
} else {
|
||||
return _w.add_value_to_object(_parent.name_, _var, _parent.obj_);
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_same<Type, Root>()) {
|
||||
return _w.value_as_root(_var);
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
if constexpr (std::is_same<Type, Map<typename W::OutputMapType>>()) {
|
||||
return _w.add_value_to_map(_parent.name_, _var, _parent.map_);
|
||||
} else if constexpr (std::is_same<Type,
|
||||
Union<typename W::OutputUnionType>>()) {
|
||||
return _w.add_value_to_union(_parent.index_, _var, _parent.union_);
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<Type>, "Unsupported option.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
37
build-config/reflect-cpp/include/rfl/parsing/Parser.hpp
Normal file
37
build-config/reflect-cpp/include/rfl/parsing/Parser.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef RFL_PARSING_PARSER_HPP_
|
||||
#define RFL_PARSING_PARSER_HPP_
|
||||
|
||||
#include "Parser_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "Parser_box.hpp"
|
||||
#include "Parser_bytestring.hpp"
|
||||
#include "Parser_c_array.hpp"
|
||||
#include "Parser_default.hpp"
|
||||
#include "Parser_default_val.hpp"
|
||||
#include "Parser_duration.hpp"
|
||||
#include "Parser_filepath.hpp"
|
||||
#include "Parser_map_like.hpp"
|
||||
#include "Parser_named_tuple.hpp"
|
||||
#include "Parser_optional.hpp"
|
||||
#include "Parser_pair.hpp"
|
||||
#include "Parser_ptr.hpp"
|
||||
#include "Parser_ref.hpp"
|
||||
#include "Parser_reference_wrapper.hpp"
|
||||
#include "Parser_rename.hpp"
|
||||
#include "Parser_result.hpp"
|
||||
#include "Parser_rfl_array.hpp"
|
||||
#include "Parser_rfl_tuple.hpp"
|
||||
#include "Parser_rfl_variant.hpp"
|
||||
#include "Parser_shared_ptr.hpp"
|
||||
#include "Parser_skip.hpp"
|
||||
#include "Parser_span.hpp"
|
||||
#include "Parser_string_view.hpp"
|
||||
#include "Parser_tagged_union.hpp"
|
||||
#include "Parser_tuple.hpp"
|
||||
#include "Parser_unique_ptr.hpp"
|
||||
#include "Parser_variant.hpp"
|
||||
#include "Parser_vector_like.hpp"
|
||||
#include "Parser_vectorstring.hpp"
|
||||
#include "Parser_wstring.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef RFL_PARSING_PARSER_ARRAY_HPP_
|
||||
#define RFL_PARSING_PARSER_ARRAY_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/ptr_cast.hpp"
|
||||
#include "ArrayReader.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "call_destructors_on_array_where_necessary.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
template <class R, class W, class T, size_t _size, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::array<T, _size>>
|
||||
struct Parser<R, W, std::array<T, _size>, ProcessorsType> {
|
||||
public:
|
||||
using InputArrayType = typename R::InputArrayType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<std::array<T, _size>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
const auto parse =
|
||||
[&](const InputArrayType& _arr) -> Result<std::array<T, _size>> {
|
||||
alignas(
|
||||
std::array<T, _size>) unsigned char buf[sizeof(std::array<T, _size>)];
|
||||
auto ptr = internal::ptr_cast<std::array<T, _size>*>(&buf);
|
||||
const auto array_reader =
|
||||
ArrayReader<R, W, ProcessorsType, T, _size>(&_r, ptr);
|
||||
auto err = _r.read_array(array_reader, _arr);
|
||||
if (err) {
|
||||
call_destructors_on_array_where_necessary(array_reader.num_set(), ptr);
|
||||
return error(*err);
|
||||
}
|
||||
err = array_reader.check_size();
|
||||
if (err) {
|
||||
call_destructors_on_array_where_necessary(array_reader.num_set(), ptr);
|
||||
return error(*err);
|
||||
}
|
||||
auto result = Result<std::array<T, _size>>(std::move(*ptr));
|
||||
call_destructors_on_array_where_necessary(array_reader.num_set(), ptr);
|
||||
return result;
|
||||
};
|
||||
|
||||
return _r.to_array(_var).and_then(parse);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::array<T, _size>& _arr,
|
||||
const P& _parent) {
|
||||
auto arr = ParentType::add_array(_w, _size, _parent);
|
||||
const auto new_parent = typename ParentType::Array{&arr};
|
||||
for (const auto& e : _arr) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, e,
|
||||
new_parent);
|
||||
}
|
||||
_w.end_array(&arr);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
return schema::Type{schema::Type::FixedSizeTypedArray{
|
||||
.size_ = _size,
|
||||
.type_ = Ref<schema::Type>::make(
|
||||
Parser<R, W, U, ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
16
build-config/reflect-cpp/include/rfl/parsing/Parser_base.hpp
Normal file
16
build-config/reflect-cpp/include/rfl/parsing/Parser_base.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef RFL_PARSING_PARSER_BASE_HPP_
|
||||
#define RFL_PARSING_PARSER_BASE_HPP_
|
||||
|
||||
#include "AreReaderAndWriter.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, T>
|
||||
struct Parser;
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
45
build-config/reflect-cpp/include/rfl/parsing/Parser_box.hpp
Normal file
45
build-config/reflect-cpp/include/rfl/parsing/Parser_box.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef RFL_PARSING_PARSER_BOX_HPP_
|
||||
#define RFL_PARSING_PARSER_BOX_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Box.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, Copyability C, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Box<T, C>>
|
||||
struct Parser<R, W, Box<T, C>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<Box<T, C>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
const auto to_box = [](auto&& _t) {
|
||||
return Box<T, C>::make(std::move(_t));
|
||||
};
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform(to_box);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const Box<T, C>& _box, const P& _parent) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_box,
|
||||
_parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef RFL_PARSING_PARSER_BYTESTRING_HPP_
|
||||
#define RFL_PARSING_PARSER_BYTESTRING_HPP_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../Bytestring.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Bytestring>
|
||||
struct Parser<R, W, Bytestring, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<Bytestring> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
return _r.template to_basic_type<Bytestring>(_var);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const Bytestring& _b, const P& _parent) {
|
||||
ParentType::add_value(_w, _b, _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
[[maybe_unused]] std::map<std::string, schema::Type>* _definitions) {
|
||||
return schema::Type{schema::Type::Bytestring{}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef RFL_PARSING_PARSER_C_ARRAY_HPP_
|
||||
#define RFL_PARSING_PARSER_C_ARRAY_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/Array.hpp"
|
||||
#include "../internal/to_std_array.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, size_t _size, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, T[_size]>
|
||||
struct Parser<R, W, T[_size], ProcessorsType> {
|
||||
public:
|
||||
using InputArrayType = typename R::InputArrayType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
using CArray = T[_size];
|
||||
|
||||
static Result<internal::Array<CArray>> read(
|
||||
const R& _r, const InputVarType& _var) noexcept {
|
||||
using StdArray = internal::to_std_array_t<CArray>;
|
||||
return Parser<R, W, StdArray, ProcessorsType>::read(_r, _var);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const CArray& _arr, const P& _parent) {
|
||||
auto arr = ParentType::add_array(_w, _size, _parent);
|
||||
const auto new_parent = typename ParentType::Array{&arr};
|
||||
for (const auto& e : _arr) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, e,
|
||||
new_parent);
|
||||
}
|
||||
_w.end_array(&arr);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using StdArray = internal::to_std_array_t<CArray>;
|
||||
return Parser<R, W, std::remove_cvref_t<StdArray>,
|
||||
ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
321
build-config/reflect-cpp/include/rfl/parsing/Parser_default.hpp
Normal file
321
build-config/reflect-cpp/include/rfl/parsing/Parser_default.hpp
Normal file
@@ -0,0 +1,321 @@
|
||||
#ifndef RFL_PARSING_PARSER_DEFAULT_HPP_
|
||||
#define RFL_PARSING_PARSER_DEFAULT_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../enums.hpp"
|
||||
#include "../from_named_tuple.hpp"
|
||||
#include "../internal/has_default_val_v.hpp"
|
||||
#include "../internal/has_reflection_method_v.hpp"
|
||||
#include "../internal/has_reflection_type_v.hpp"
|
||||
#include "../internal/has_reflector.hpp"
|
||||
#include "../internal/is_basic_type.hpp"
|
||||
#include "../internal/is_description.hpp"
|
||||
#include "../internal/is_literal.hpp"
|
||||
#include "../internal/is_underlying_enums_v.hpp"
|
||||
#include "../internal/is_validator.hpp"
|
||||
#include "../internal/processed_t.hpp"
|
||||
#include "../internal/ptr_cast.hpp"
|
||||
#include "../internal/to_ptr_named_tuple.hpp"
|
||||
#include "../thirdparty/enchantum/enchantum.hpp"
|
||||
#include "../to_view.hpp"
|
||||
#include "AreReaderAndWriter.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "call_destructors_where_necessary.hpp"
|
||||
#include "is_tagged_union_wrapper.hpp"
|
||||
#include "make_type_name.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
/// Default case - anything that cannot be explicitly matched.
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, T>
|
||||
struct Parser {
|
||||
public:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
/// Expresses the variables as type T.
|
||||
static Result<T> read(const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (internal::has_read_reflector<T>) {
|
||||
const auto wrap_in_t = [](auto&& _named_tuple) -> Result<T> {
|
||||
try {
|
||||
using NT = decltype(_named_tuple);
|
||||
return Reflector<T>::to(std::forward<NT>(_named_tuple));
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
};
|
||||
return Parser<R, W, typename Reflector<T>::ReflType,
|
||||
ProcessorsType>::read(_r, _var)
|
||||
.and_then(wrap_in_t);
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulReader<R> &&
|
||||
internal::is_literal_v<T>) {
|
||||
return _r.template to_basic_type<T>(_var);
|
||||
|
||||
} else if constexpr (R::template has_custom_constructor<T>) {
|
||||
return _r.template use_custom_constructor<T>(_var);
|
||||
|
||||
} else {
|
||||
if constexpr (internal::has_reflection_type_v<T>) {
|
||||
using ReflectionType = std::remove_cvref_t<typename T::ReflectionType>;
|
||||
const auto wrap_in_t = [](auto&& _named_tuple) -> Result<T> {
|
||||
try {
|
||||
using NT = decltype(_named_tuple);
|
||||
return T{std::forward<NT>(_named_tuple)};
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
};
|
||||
return Parser<R, W, ReflectionType, ProcessorsType>::read(_r, _var)
|
||||
.and_then(wrap_in_t);
|
||||
|
||||
} else if constexpr (std::is_class_v<T> && std::is_aggregate_v<T>) {
|
||||
if constexpr (ProcessorsType::default_if_missing_ ||
|
||||
internal::has_default_val_v<T>) {
|
||||
return read_struct_with_default(_r, _var);
|
||||
} else {
|
||||
return read_struct(_r, _var);
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_enum_v<T>) {
|
||||
if constexpr (ProcessorsType::underlying_enums_ ||
|
||||
schemaful::IsSchemafulReader<R>) {
|
||||
static_assert(enchantum::ScopedEnum<T>,
|
||||
"The enum must be a scoped enum in order to retrieve "
|
||||
"the underlying value.");
|
||||
return _r.template to_basic_type<std::underlying_type_t<T>>(_var)
|
||||
.transform([](const auto _val) { return static_cast<T>(_val); });
|
||||
} else {
|
||||
return _r.template to_basic_type<std::string>(_var).and_then(
|
||||
rfl::string_to_enum<T>);
|
||||
}
|
||||
|
||||
} else {
|
||||
return _r.template to_basic_type<std::remove_cvref_t<T>>(_var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const T& _var, const P& _parent) {
|
||||
if constexpr (internal::has_write_reflector<T>) {
|
||||
Parser<R, W, typename Reflector<T>::ReflType, ProcessorsType>::write(
|
||||
_w, Reflector<T>::from(_var), _parent);
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W> &&
|
||||
internal::is_literal_v<T>) {
|
||||
ParentType::add_value(_w, _var, _parent);
|
||||
|
||||
} else if constexpr (internal::has_reflection_type_v<T>) {
|
||||
using ReflectionType = std::remove_cvref_t<typename T::ReflectionType>;
|
||||
if constexpr (internal::has_reflection_method_v<T>) {
|
||||
Parser<R, W, ReflectionType, ProcessorsType>::write(
|
||||
_w, _var.reflection(), _parent);
|
||||
} else {
|
||||
const auto& [r] = _var;
|
||||
Parser<R, W, ReflectionType, ProcessorsType>::write(_w, r, _parent);
|
||||
}
|
||||
|
||||
} else if constexpr (std::is_class_v<T> && std::is_aggregate_v<T>) {
|
||||
const auto ptr_named_tuple = ProcessorsType::template process<T>(
|
||||
internal::to_ptr_named_tuple(_var));
|
||||
using PtrNamedTupleType = std::remove_cvref_t<decltype(ptr_named_tuple)>;
|
||||
Parser<R, W, PtrNamedTupleType, ProcessorsType>::write(
|
||||
_w, ptr_named_tuple, _parent);
|
||||
|
||||
} else if constexpr (std::is_enum_v<T>) {
|
||||
if constexpr (ProcessorsType::underlying_enums_ ||
|
||||
schemaful::IsSchemafulWriter<W>) {
|
||||
const auto val = static_cast<std::underlying_type_t<T>>(_var);
|
||||
ParentType::add_value(_w, val, _parent);
|
||||
} else {
|
||||
const auto str = rfl::enum_to_string(_var);
|
||||
ParentType::add_value(_w, str, _parent);
|
||||
}
|
||||
|
||||
} else {
|
||||
ParentType::add_value(_w, _var, _parent);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a schema for the underlying type.
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
using Type = schema::Type;
|
||||
if constexpr (std::is_same<U, bool>()) {
|
||||
return Type{Type::Boolean{}};
|
||||
|
||||
} else if constexpr (std::is_same<U, std::int32_t>()) {
|
||||
return Type{Type::Int32{}};
|
||||
|
||||
} else if constexpr (std::is_same<U, std::int64_t>()) {
|
||||
return Type{Type::Int64{}};
|
||||
|
||||
} else if constexpr (std::is_same<U, std::uint32_t>()) {
|
||||
return Type{Type::UInt32{}};
|
||||
|
||||
} else if constexpr (std::is_same<U, std::uint64_t>()) {
|
||||
return Type{Type::UInt64{}};
|
||||
|
||||
} else if constexpr (std::is_integral<U>()) {
|
||||
return Type{Type::Integer{}};
|
||||
|
||||
} else if constexpr (std::is_same<U, float>()) {
|
||||
return Type{Type::Float{}};
|
||||
|
||||
} else if constexpr (std::is_floating_point_v<U>) {
|
||||
return Type{Type::Double{}};
|
||||
|
||||
} else if constexpr (std::is_same<U, std::string>()) {
|
||||
return Type{Type::String{}};
|
||||
|
||||
} else if constexpr (rfl::internal::is_description_v<U>) {
|
||||
return make_description<U>(_definitions);
|
||||
|
||||
} else if constexpr (std::is_enum_v<U>) {
|
||||
return make_enum<U>(_definitions);
|
||||
|
||||
} else if constexpr (std::is_class_v<U> && std::is_aggregate_v<U>) {
|
||||
return make_reference<U>(_definitions);
|
||||
|
||||
} else if constexpr (internal::is_literal_v<U>) {
|
||||
return Type{Type::Literal{.values_ = U::strings()}};
|
||||
|
||||
} else if constexpr (internal::is_validator_v<U>) {
|
||||
return make_validated<U>(_definitions);
|
||||
|
||||
} else if constexpr (internal::has_reflection_type_v<U> ||
|
||||
internal::has_read_reflector<U> ||
|
||||
internal::has_write_reflector<U>) {
|
||||
return make_reference<U>(_definitions);
|
||||
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<U>, "Unsupported type.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U>
|
||||
static schema::Type make_description(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using Type = schema::Type;
|
||||
return Type{Type::Description{
|
||||
.description_ = typename U::Content().str(),
|
||||
.type_ =
|
||||
Ref<Type>::make(Parser<R, W, std::remove_cvref_t<typename U::Type>,
|
||||
ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
|
||||
template <class U>
|
||||
static schema::Type make_enum(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using Type = schema::Type;
|
||||
if constexpr (ProcessorsType::underlying_enums_ ||
|
||||
schemaful::IsSchemafulReader<R>) {
|
||||
return Type{Type::Integer{}};
|
||||
} else if constexpr (enchantum::is_bitflag<U>) {
|
||||
return Type{Type::String{}};
|
||||
} else {
|
||||
return Parser<
|
||||
R, W,
|
||||
typename decltype(internal::enums::get_enum_names<U>())::Literal,
|
||||
ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
}
|
||||
|
||||
template <class U>
|
||||
static schema::Type make_reference(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using Type = schema::Type;
|
||||
const auto name = make_type_name<U>();
|
||||
|
||||
if (_definitions->find(name) == _definitions->end()) {
|
||||
(*_definitions)[name] =
|
||||
Type{Type::Integer{}}; // Placeholder to avoid infinite loop.
|
||||
|
||||
if constexpr (internal::has_reflection_type_v<U>) {
|
||||
(*_definitions)[name] =
|
||||
Parser<R, W, typename U::ReflectionType, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
|
||||
} else if constexpr (internal::has_read_reflector<U> ||
|
||||
internal::has_write_reflector<U>) {
|
||||
(*_definitions)[name] = Parser<R, W, typename Reflector<U>::ReflType,
|
||||
ProcessorsType>::to_schema(_definitions);
|
||||
|
||||
} else {
|
||||
using NamedTupleType = internal::processed_t<U, ProcessorsType>;
|
||||
(*_definitions)[name] =
|
||||
Parser<R, W, NamedTupleType, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
}
|
||||
return Type{Type::Reference{name}};
|
||||
}
|
||||
|
||||
template <class U>
|
||||
static schema::Type make_validated(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using Type = schema::Type;
|
||||
using ReflectionType = std::remove_cvref_t<typename U::ReflectionType>;
|
||||
using ValidationType = std::remove_cvref_t<typename U::ValidationType>;
|
||||
return Type{Type::Validated{
|
||||
.type_ = Ref<Type>::make(
|
||||
Parser<R, W, ReflectionType, ProcessorsType>::to_schema(
|
||||
_definitions)),
|
||||
.validation_ = ValidationType::template to_schema<ReflectionType>()}};
|
||||
}
|
||||
|
||||
/// The way this works is that we allocate space on the stack in this size of
|
||||
/// the struct 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.
|
||||
static Result<T> read_struct(const R& _r, const InputVarType& _var) {
|
||||
alignas(T) unsigned char buf[sizeof(T)]{};
|
||||
auto ptr = internal::ptr_cast<T*>(&buf);
|
||||
auto view = ProcessorsType::template process<T>(to_view(*ptr));
|
||||
using ViewType = std::remove_cvref_t<decltype(view)>;
|
||||
const auto [set, err] =
|
||||
Parser<R, W, ViewType, ProcessorsType>::read_view(_r, _var, &view);
|
||||
if (err) [[unlikely]] {
|
||||
call_destructors_where_necessary(set, &view);
|
||||
return error(err->what());
|
||||
}
|
||||
auto res = Result<T>(std::move(*ptr));
|
||||
call_destructors_where_necessary(set, &view);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// This is actually more straight-forward than the standard case - we just
|
||||
/// allocate a struct and then fill it. But it is less efficient and it
|
||||
/// assumes that all values on the struct have a default constructor, so we
|
||||
/// only use it when the DefaultIfMissing preprocessor is added.
|
||||
static Result<T> read_struct_with_default(const R& _r,
|
||||
const InputVarType& _var) {
|
||||
auto t = T{};
|
||||
auto view = ProcessorsType::template process<T>(to_view(t));
|
||||
using ViewType = decltype(view);
|
||||
const auto err =
|
||||
Parser<R, W, ViewType, ProcessorsType>::read_view_with_default(_r, _var,
|
||||
&view);
|
||||
if (err) [[unlikely]] {
|
||||
return error(*err);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef RFL_PARSING_PARSER_DEFAULTVAL_HPP_
|
||||
#define RFL_PARSING_PARSER_DEFAULTVAL_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../DefaultVal.hpp"
|
||||
#include "AreReaderAndWriter.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, DefaultVal<T>>
|
||||
struct Parser<R, W, DefaultVal<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<DefaultVal<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform([](auto&& _t) {
|
||||
return DefaultVal<T>(std::forward<decltype(_t)>(_t));
|
||||
});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const DefaultVal<T>& _d, const P& _parent) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, _d.value(),
|
||||
_parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
return schema::Type{
|
||||
Parser<R, W, U, ProcessorsType>::to_schema(_definitions)};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
143
build-config/reflect-cpp/include/rfl/parsing/Parser_duration.hpp
Normal file
143
build-config/reflect-cpp/include/rfl/parsing/Parser_duration.hpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifndef RFL_PARSING_PARSER_DURATION_HPP_
|
||||
#define RFL_PARSING_PARSER_DURATION_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Literal.hpp"
|
||||
#include "../Variant.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl ::parsing {
|
||||
|
||||
template <class R, class W, class Rep, class Period, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::chrono::duration<Rep, Period>>
|
||||
struct Parser<R, W, std::chrono::duration<Rep, Period>, ProcessorsType> {
|
||||
public:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
using DurationType = std::chrono::duration<Rep, Period>;
|
||||
|
||||
using Unit = Literal<"nanoseconds", "microseconds", "milliseconds", "seconds",
|
||||
"minutes", "hours", "days", "weeks", "months", "years">;
|
||||
|
||||
using SupportedTypes =
|
||||
Variant<std::chrono::nanoseconds, std::chrono::microseconds,
|
||||
std::chrono::milliseconds, std::chrono::seconds,
|
||||
std::chrono::minutes, std::chrono::hours, std::chrono::days,
|
||||
std::chrono::weeks, std::chrono::months, std::chrono::years>;
|
||||
|
||||
struct RType {
|
||||
int64_t count;
|
||||
Unit unit;
|
||||
};
|
||||
|
||||
static Result<DurationType> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
return Parser<R, W, RType, ProcessorsType>::read(_r, _var)
|
||||
.and_then(to_duration)
|
||||
.transform([](auto&& _duration) {
|
||||
return _duration.visit([](auto&& _d) -> DurationType {
|
||||
return std::chrono::duration_cast<DurationType>(std::move(_d));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const DurationType& _d, const P& _parent) {
|
||||
const auto r =
|
||||
RType{.count = static_cast<int64_t>(_d.count()), .unit = make_unit()};
|
||||
return Parser<R, W, RType, ProcessorsType>::write(_w, r, _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, RType, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
|
||||
private:
|
||||
static Result<SupportedTypes> to_duration(const RType& _r) {
|
||||
switch (_r.unit.value()) {
|
||||
case Unit::value_of<"nanoseconds">():
|
||||
return SupportedTypes(std::chrono::nanoseconds(_r.count));
|
||||
|
||||
case Unit::value_of<"microseconds">():
|
||||
return SupportedTypes(std::chrono::microseconds(_r.count));
|
||||
|
||||
case Unit::value_of<"milliseconds">():
|
||||
return SupportedTypes(std::chrono::milliseconds(_r.count));
|
||||
|
||||
case Unit::value_of<"seconds">():
|
||||
return SupportedTypes(std::chrono::seconds(_r.count));
|
||||
|
||||
case Unit::value_of<"minutes">():
|
||||
return SupportedTypes(std::chrono::minutes(_r.count));
|
||||
|
||||
case Unit::value_of<"hours">():
|
||||
return SupportedTypes(std::chrono::hours(_r.count));
|
||||
|
||||
case Unit::value_of<"days">():
|
||||
return SupportedTypes(std::chrono::days(_r.count));
|
||||
|
||||
case Unit::value_of<"weeks">():
|
||||
return SupportedTypes(std::chrono::weeks(_r.count));
|
||||
|
||||
case Unit::value_of<"months">():
|
||||
return SupportedTypes(std::chrono::months(_r.count));
|
||||
|
||||
case Unit::value_of<"years">():
|
||||
return SupportedTypes(std::chrono::years(_r.count));
|
||||
|
||||
default:
|
||||
return error("Unsupported unit.");
|
||||
}
|
||||
}
|
||||
|
||||
static auto make_unit() noexcept {
|
||||
if constexpr (std::is_same_v<DurationType, std::chrono::nanoseconds>) {
|
||||
return Unit::make<"nanoseconds">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType,
|
||||
std::chrono::microseconds>) {
|
||||
return Unit::make<"microseconds">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType,
|
||||
std::chrono::milliseconds>) {
|
||||
return Unit::make<"milliseconds">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::seconds>) {
|
||||
return Unit::make<"seconds">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::minutes>) {
|
||||
return Unit::make<"minutes">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::hours>) {
|
||||
return Unit::make<"hours">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::days>) {
|
||||
return Unit::make<"days">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::weeks>) {
|
||||
return Unit::make<"weeks">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::months>) {
|
||||
return Unit::make<"months">();
|
||||
|
||||
} else if constexpr (std::is_same_v<DurationType, std::chrono::years>) {
|
||||
return Unit::make<"years">();
|
||||
|
||||
} else {
|
||||
static_assert(always_false_v<DurationType>, "Unsupported type.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef RFL_PARSING_PARSER_FILEPATH_HPP_
|
||||
#define RFL_PARSING_PARSER_FILEPATH_HPP_
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::filesystem::path>
|
||||
struct Parser<R, W, std::filesystem::path, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
/// Expresses the variables as type T.
|
||||
static Result<std::filesystem::path> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
const auto to_path =
|
||||
[&](std::string&& _str) -> Result<std::filesystem::path> {
|
||||
try {
|
||||
return std::filesystem::path(_str);
|
||||
} catch (std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
};
|
||||
return Parser<R, W, std::string, ProcessorsType>::read(_r, _var).and_then(
|
||||
to_path);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::filesystem::path& _p,
|
||||
const P& _parent) {
|
||||
return Parser<R, W, std::string, ProcessorsType>::write(_w, _p.string(),
|
||||
_parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::string, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef RFL_PARSING_PARSER_MAP_LIKE_HPP_
|
||||
#define RFL_PARSING_PARSER_MAP_LIKE_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../ExtraFields.hpp"
|
||||
#include "../Object.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "MapParser.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::map<std::string, T>>
|
||||
struct Parser<R, W, std::map<std::string, T>, ProcessorsType>
|
||||
: public MapParser<R, W, std::map<std::string, T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class Hash, class KeyEqual,
|
||||
class Allocator, class ProcessorsType>
|
||||
requires AreReaderAndWriter<
|
||||
R, W, std::unordered_map<std::string, T, Hash, KeyEqual, Allocator>>
|
||||
struct Parser<R, W,
|
||||
std::unordered_map<std::string, T, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType>
|
||||
: public MapParser<
|
||||
R, W, std::unordered_map<std::string, T, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Object<T>>
|
||||
struct Parser<R, W, Object<T>, ProcessorsType>
|
||||
: public MapParser<R, W, Object<T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, ExtraFields<T>>
|
||||
struct Parser<R, W, ExtraFields<T>, ProcessorsType>
|
||||
: public MapParser<R, W, ExtraFields<T>, ProcessorsType> {};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef RFL_PARSING_PARSER_NAMED_TUPLE_HPP_
|
||||
#define RFL_PARSING_PARSER_NAMED_TUPLE_HPP_
|
||||
|
||||
#include "NamedTupleParser.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class... FieldTypes, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, NamedTuple<FieldTypes...>>
|
||||
struct Parser<R, W, NamedTuple<FieldTypes...>, ProcessorsType>
|
||||
: public NamedTupleParser<
|
||||
R, W, /*_ignore_empty_containers=*/false,
|
||||
/*_all_required=*/ProcessorsType::all_required_,
|
||||
/*_no_field_names=*/ProcessorsType::no_field_names_, ProcessorsType,
|
||||
FieldTypes...> {
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef RFL_PARSING_PARSER_OPTIONAL_HPP_
|
||||
#define RFL_PARSING_PARSER_OPTIONAL_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "schemaful/OptionalReader.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::optional<T>>
|
||||
struct Parser<R, W, std::optional<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<std::optional<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using O = schemaful::OptionalReader<R, W, std::remove_cvref_t<T>,
|
||||
ProcessorsType>;
|
||||
const auto to_optional = [&](const auto& _u) -> Result<std::optional<T>> {
|
||||
return _r.template read_union<std::optional<T>, O>(_u);
|
||||
};
|
||||
return _r.to_union(_var).and_then(to_optional);
|
||||
} else {
|
||||
if (_r.is_empty(_var)) {
|
||||
return std::optional<T>();
|
||||
}
|
||||
const auto to_opt = [](auto&& _t) {
|
||||
return std::make_optional<T>(std::forward<decltype(_t)>(_t));
|
||||
};
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r,
|
||||
_var)
|
||||
.transform(to_opt);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::optional<T>& _o, const P& _parent) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
auto u = ParentType::add_union(_w, _parent);
|
||||
using UnionType = typename ParentType::template Union<decltype(u)>;
|
||||
auto p =
|
||||
UnionType{.index_ = static_cast<size_t>(_o ? 0 : 1), .union_ = &u};
|
||||
if (_o) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_o, p);
|
||||
} else {
|
||||
ParentType::add_null(_w, p);
|
||||
}
|
||||
} else {
|
||||
if (_o) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_o,
|
||||
_parent);
|
||||
} else {
|
||||
ParentType::add_null(_w, _parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
return schema::Type{schema::Type::Optional{Ref<schema::Type>::make(
|
||||
Parser<R, W, U, ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
52
build-config/reflect-cpp/include/rfl/parsing/Parser_pair.hpp
Normal file
52
build-config/reflect-cpp/include/rfl/parsing/Parser_pair.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef RFL_PARSING_PARSER_PAIR_HPP_
|
||||
#define RFL_PARSING_PARSER_PAIR_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class FirstType, class SecondType,
|
||||
class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::pair<FirstType, SecondType>>
|
||||
struct Parser<R, W, std::pair<FirstType, SecondType>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
/// Expresses the variables as type T.
|
||||
static Result<std::pair<FirstType, SecondType>> read(
|
||||
const R& _r, const InputVarType& _var) noexcept {
|
||||
const auto to_pair = [&](auto&& _t) {
|
||||
return std::make_pair(std::move(std::get<0>(_t)),
|
||||
std::move(std::get<1>(_t)));
|
||||
};
|
||||
return Parser<R, W, std::tuple<FirstType, SecondType>,
|
||||
ProcessorsType>::read(_r, _var)
|
||||
.transform(to_pair);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::pair<FirstType, SecondType>& _p,
|
||||
const P& _parent) {
|
||||
const auto tup = std::make_tuple(&_p.first, &_p.second);
|
||||
Parser<R, W, std::tuple<const FirstType*, const SecondType*>,
|
||||
ProcessorsType>::write(_w, tup, _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::tuple<FirstType, SecondType>,
|
||||
ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
69
build-config/reflect-cpp/include/rfl/parsing/Parser_ptr.hpp
Normal file
69
build-config/reflect-cpp/include/rfl/parsing/Parser_ptr.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef RFL_PARSING_PARSER_PTR_HPP_
|
||||
#define RFL_PARSING_PARSER_PTR_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, T*>
|
||||
struct Parser<R, W, T*, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
/// Expresses the variables as type T.
|
||||
static Result<T*> read(const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (!ProcessorsType::allow_raw_ptrs_) {
|
||||
static_assert(
|
||||
always_false_v<T>,
|
||||
"Reading into raw pointers is dangerous and "
|
||||
"therefore unsupported by default. "
|
||||
"Please consider using std::unique_ptr, rfl::Box, "
|
||||
"std::shared_ptr, "
|
||||
"rfl::Ref or std::optional instead. "
|
||||
"If you absolutely must use raw pointers, "
|
||||
"you can pass the rfl::AllowRawPtrs processor. "
|
||||
"Please note that it is then YOUR responsibility "
|
||||
"to delete the allocated memory. Please also refer "
|
||||
"to the related documentation (in the section on processors).");
|
||||
return error("Unsupported.");
|
||||
} else {
|
||||
if (_r.is_empty(_var)) {
|
||||
return nullptr;
|
||||
}
|
||||
return Parser<R, W, T, ProcessorsType>::read(_r, _var).transform(
|
||||
[](T&& _t) { return new T(std::move(_t)); });
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const T* _ptr, const P& _parent) {
|
||||
if (!_ptr) {
|
||||
ParentType::add_null(_w, _parent);
|
||||
return;
|
||||
}
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_ptr,
|
||||
_parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
43
build-config/reflect-cpp/include/rfl/parsing/Parser_ref.hpp
Normal file
43
build-config/reflect-cpp/include/rfl/parsing/Parser_ref.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef RFL_PARSING_PARSER_REF_HPP_
|
||||
#define RFL_PARSING_PARSER_REF_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Ref<T>>
|
||||
struct Parser<R, W, Ref<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<Ref<T>> read(const R& _r, const InputVarType& _var) noexcept {
|
||||
const auto to_ref = [&](auto&& _t) { return Ref<T>::make(std::move(_t)); };
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform(to_ref);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const Ref<T>& _ref, const P& _parent) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_ref,
|
||||
_parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef RFL_PARSING_PARSER_REFERENCE_WRAPPER_HPP_
|
||||
#define RFL_PARSING_PARSER_REFERENCE_WRAPPER_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::reference_wrapper<T>>
|
||||
struct Parser<R, W, std::reference_wrapper<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<std::reference_wrapper<T>> read(const R&,
|
||||
const InputVarType&) noexcept {
|
||||
static_assert(always_false_v<T>,
|
||||
"Reading into std::reference_wrapper is dangerous and "
|
||||
"therefore unsupported. "
|
||||
"Please consider using std::unique_ptr, rfl::Box, "
|
||||
"std::shared_ptr, or"
|
||||
"rfl::Ref instead.");
|
||||
return error("Unsupported.");
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::reference_wrapper<T> _ref,
|
||||
const P& _p) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, _ref.get(),
|
||||
_p);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef RFL_PARSING_PARSER_RENAME_HPP_
|
||||
#define RFL_PARSING_PARSER_RENAME_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Rename.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, internal::StringLiteral _name,
|
||||
class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Rename<_name, T>>
|
||||
struct Parser<R, W, Rename<_name, T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<Rename<_name, T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
const auto to_rename = [](auto&& _t) {
|
||||
return Rename<_name, T>(std::move(_t));
|
||||
};
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform(to_rename);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const Rename<_name, T>& _rename,
|
||||
const P& _parent) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(
|
||||
_w, _rename.value(), _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef RFL_PARSING_PARSER_RESULT_HPP_
|
||||
#define RFL_PARSING_PARSER_RESULT_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Field.hpp"
|
||||
#include "../NamedTuple.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Result<T>>
|
||||
struct Parser<R, W, Result<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ErrorType = NamedTuple<Field<"error", std::string>>;
|
||||
using VariantType = std::variant<std::remove_cvref_t<T>, ErrorType>;
|
||||
|
||||
static Result<Result<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
const auto handle = [](auto&& _t) -> Result<T> {
|
||||
using Type = std::remove_cvref_t<decltype(_t)>;
|
||||
if constexpr (std::is_same<Type, ErrorType>()) {
|
||||
return error(_t.template get<"error">());
|
||||
} else {
|
||||
return std::forward<std::remove_cvref_t<T>>(_t);
|
||||
}
|
||||
};
|
||||
|
||||
const auto to_res = [&](VariantType&& _v) -> Result<T> {
|
||||
return std::visit(handle, std::forward<VariantType>(_v));
|
||||
};
|
||||
|
||||
return Result<Result<T>>(
|
||||
Parser<R, W, VariantType, ProcessorsType>::read(_r, _var).transform(
|
||||
to_res));
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const Result<T>& _r, const P& _parent) {
|
||||
if (_r) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(
|
||||
_w, _r.value(), _parent);
|
||||
} else {
|
||||
Parser<R, W, ErrorType, ProcessorsType>::write(
|
||||
_w, ErrorType(make_field<"error">(_r.error().what())), _parent);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef RFL_PARSING_PARSER_RFL_ARRAY_HPP_
|
||||
#define RFL_PARSING_PARSER_RFL_ARRAY_HPP_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/Array.hpp"
|
||||
#include "../internal/to_std_array.hpp"
|
||||
#include "Parser_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, internal::Array<T>>
|
||||
struct Parser<R, W, internal::Array<T>, ProcessorsType> {
|
||||
public:
|
||||
using InputArrayType = typename R::InputArrayType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using StdArray = internal::to_std_array_t<T>;
|
||||
|
||||
static Result<internal::Array<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
return Parser<R, W, StdArray, ProcessorsType>::read(_r, _var);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const internal::Array<T>& _arr,
|
||||
const P& _parent) {
|
||||
Parser<R, W, StdArray, ProcessorsType>::write(_w, _arr.arr_, _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, StdArray, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef RFL_PARSING_PARSER_RFL_TUPLE_HPP_
|
||||
#define RFL_PARSING_PARSER_RFL_TUPLE_HPP_
|
||||
|
||||
#include "../Tuple.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "TupleParser.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class... Ts, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, rfl::Tuple<Ts...>>
|
||||
struct Parser<R, W, rfl::Tuple<Ts...>, ProcessorsType>
|
||||
: public TupleParser<R, W, /*_ignore_empty_containers=*/false,
|
||||
/*_all_required=*/ProcessorsType::all_required_,
|
||||
ProcessorsType, rfl::Tuple<Ts...>> {
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,217 @@
|
||||
#ifndef RFL_PARSING_PARSER_RFL_VARIANT_HPP_
|
||||
#define RFL_PARSING_PARSER_RFL_VARIANT_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Variant.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/all_fields.hpp"
|
||||
#include "../internal/nth_element_t.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 <class R, class W, class... AlternativeTypes, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, rfl::Variant<AlternativeTypes...>>
|
||||
class Parser<R, W, rfl::Variant<AlternativeTypes...>, ProcessorsType> {
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
public:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<rfl::Variant<AlternativeTypes...>> read(
|
||||
const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using WrappedType = rfl::Variant<NamedTuple<AlternativeTypes>...>;
|
||||
return Parser<R, W, WrappedType, ProcessorsType>::read(_r, _var)
|
||||
.transform(
|
||||
[](auto&& _variant) -> rfl::Variant<AlternativeTypes...> {
|
||||
return std::move(_variant).visit([](auto&& _named_tuple) {
|
||||
return rfl::Variant<AlternativeTypes...>(std::move(
|
||||
std::move(_named_tuple).fields().template get<0>()));
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
return FieldVariantParser<R, W, ProcessorsType,
|
||||
AlternativeTypes...>::read(_r, _var);
|
||||
}
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using V = schemaful::VariantReader<R, W, Variant<AlternativeTypes...>,
|
||||
ProcessorsType, AlternativeTypes...>;
|
||||
return _r.to_union(_var).and_then([&](const auto& _u) {
|
||||
return _r.template read_union<Variant<AlternativeTypes...>, 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<AlternativeTypes, remove_namespaces>...>;
|
||||
const auto from_field_variant =
|
||||
[](auto&& _field) -> rfl::Variant<AlternativeTypes...> {
|
||||
return std::move(_field.value());
|
||||
};
|
||||
return Parser<R, W, FieldVariantType, ProcessorsType>::read(_r, _var)
|
||||
.transform([&](FieldVariantType&& _f) {
|
||||
return _f.visit(from_field_variant);
|
||||
});
|
||||
|
||||
} else {
|
||||
std::optional<rfl::Variant<AlternativeTypes...>> result;
|
||||
std::vector<Error> errors;
|
||||
errors.reserve(sizeof...(AlternativeTypes));
|
||||
read_variant(
|
||||
_r, _var, &result, &errors,
|
||||
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
|
||||
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 <class P>
|
||||
static void write(const W& _w,
|
||||
const rfl::Variant<AlternativeTypes...>& _variant,
|
||||
const P& _parent) {
|
||||
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
using WrappedType = rfl::Variant<
|
||||
NamedTuple<Field<AlternativeTypes::name_,
|
||||
const typename AlternativeTypes::Type*>>...>;
|
||||
const auto to_wrapped = [](const auto& _variant) -> WrappedType {
|
||||
return _variant.visit([](const auto& _field) -> WrappedType {
|
||||
return make_named_tuple(internal::to_ptr_field(_field));
|
||||
});
|
||||
};
|
||||
Parser<R, W, WrappedType, ProcessorsType>::write(
|
||||
_w, to_wrapped(_variant), _parent);
|
||||
|
||||
} else {
|
||||
FieldVariantParser<R, W, ProcessorsType, AlternativeTypes...>::write(
|
||||
_w, _variant, _parent);
|
||||
}
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
return rfl::visit(
|
||||
[&](const auto& _v) {
|
||||
using Type = std::remove_cvref_t<decltype(_v)>;
|
||||
auto u = ParentType::add_union(_w, _parent);
|
||||
using UnionType = typename ParentType::template Union<decltype(u)>;
|
||||
auto p = UnionType{.index_ = static_cast<size_t>(_variant.index()),
|
||||
.union_ = &u};
|
||||
Parser<R, W, Type, ProcessorsType>::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<VariantAlternativeWrapper<const AlternativeTypes*,
|
||||
remove_namespaces>...>;
|
||||
const auto to_field_variant =
|
||||
[]<class T>(const T& _t) -> FieldVariantType {
|
||||
return VariantAlternativeWrapper<const T*, remove_namespaces>(&_t);
|
||||
};
|
||||
Parser<R, W, FieldVariantType, ProcessorsType>::write(
|
||||
_w, _variant.visit(to_field_variant), _parent);
|
||||
|
||||
} else {
|
||||
const auto handle = [&](const auto& _v) {
|
||||
using Type = std::remove_cvref_t<decltype(_v)>;
|
||||
Parser<R, W, Type, ProcessorsType>::write(_w, _v, _parent);
|
||||
};
|
||||
return rfl::visit(handle, _variant);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
|
||||
return FieldVariantParser<R, W, ProcessorsType,
|
||||
AlternativeTypes...>::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<AlternativeTypes, remove_namespaces>...>;
|
||||
return Parser<R, W, FieldVariantType, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
|
||||
} else {
|
||||
std::vector<schema::Type> types;
|
||||
build_schema(
|
||||
_definitions, &types,
|
||||
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
|
||||
return schema::Type{schema::Type::AnyOf{.types_ = std::move(types)}};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t _i>
|
||||
static void add_to_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
std::vector<schema::Type>* _types) noexcept {
|
||||
using AltType =
|
||||
std::remove_cvref_t<internal::nth_element_t<_i, AlternativeTypes...>>;
|
||||
_types->push_back(
|
||||
Parser<R, W, AltType, ProcessorsType>::to_schema(_definitions));
|
||||
}
|
||||
|
||||
template <int... _is>
|
||||
static void build_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
std::vector<schema::Type>* _types,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
(add_to_schema<_is>(_definitions, _types), ...);
|
||||
}
|
||||
|
||||
template <int _i>
|
||||
static void read_one_alternative(
|
||||
const R& _r, const InputVarType& _var,
|
||||
std::optional<rfl::Variant<AlternativeTypes...>>* _result,
|
||||
std::vector<Error>* _errors) noexcept {
|
||||
if (!*_result) {
|
||||
using AltType =
|
||||
std::remove_cvref_t<internal::nth_element_t<_i, AlternativeTypes...>>;
|
||||
auto res = Parser<R, W, AltType, ProcessorsType>::read(_r, _var);
|
||||
if (res) {
|
||||
*_result = std::move(*res);
|
||||
} else {
|
||||
_errors->emplace_back(std::move(res.error()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int... _is>
|
||||
static void read_variant(
|
||||
const R& _r, const InputVarType& _var,
|
||||
std::optional<rfl::Variant<AlternativeTypes...>>* _result,
|
||||
std::vector<Error>* _errors,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
(read_one_alternative<_is>(_r, _var, _result, _errors), ...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef RFL_PARSING_PARSER_SHARED_PTR_HPP_
|
||||
#define RFL_PARSING_PARSER_SHARED_PTR_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "schemaful/SharedPtrReader.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::shared_ptr<T>>
|
||||
struct Parser<R, W, std::shared_ptr<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<std::shared_ptr<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using S = schemaful::SharedPtrReader<R, W, std::remove_cvref_t<T>,
|
||||
ProcessorsType>;
|
||||
const auto to_shared = [&](const auto& _u) -> Result<std::shared_ptr<T>> {
|
||||
return _r.template read_union<std::shared_ptr<T>, S>(_u);
|
||||
};
|
||||
return _r.to_union(_var).and_then(to_shared);
|
||||
} else {
|
||||
if (_r.is_empty(_var)) {
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r,
|
||||
_var)
|
||||
.transform([](T&& _t) { return std::make_shared<T>(std::move(_t)); });
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::shared_ptr<T>& _s,
|
||||
const P& _parent) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
auto u = ParentType::add_union(_w, _parent);
|
||||
using UnionType = typename ParentType::template Union<decltype(u)>;
|
||||
auto p =
|
||||
UnionType{.index_ = static_cast<size_t>(_s ? 0 : 1), .union_ = &u};
|
||||
if (_s) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_s, p);
|
||||
} else {
|
||||
ParentType::add_null(_w, p);
|
||||
}
|
||||
} else {
|
||||
if (_s) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_s,
|
||||
_parent);
|
||||
} else {
|
||||
ParentType::add_null(_w, _parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
return schema::Type{schema::Type::Optional{Ref<schema::Type>::make(
|
||||
Parser<R, W, U, ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
66
build-config/reflect-cpp/include/rfl/parsing/Parser_skip.hpp
Normal file
66
build-config/reflect-cpp/include/rfl/parsing/Parser_skip.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef RFL_PARSING_PARSER_SKIP_HPP_
|
||||
#define RFL_PARSING_PARSER_SKIP_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/Skip.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, bool _skip_serialization,
|
||||
bool _skip_deserialization, class ProcessorsType>
|
||||
requires AreReaderAndWriter<
|
||||
R, W, internal::Skip<T, _skip_serialization, _skip_deserialization>>
|
||||
struct Parser<R, W,
|
||||
internal::Skip<T, _skip_serialization, _skip_deserialization>,
|
||||
ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<internal::Skip<T, _skip_serialization, _skip_deserialization>>
|
||||
read(const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (_skip_deserialization) {
|
||||
return internal::Skip<T, _skip_serialization, _skip_deserialization>(
|
||||
std::remove_cvref_t<T>());
|
||||
} else {
|
||||
const auto to_skip = [&](auto&& _t) {
|
||||
return internal::Skip<T, _skip_serialization, _skip_deserialization>(
|
||||
std::move(_t));
|
||||
};
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r,
|
||||
_var)
|
||||
.transform(to_skip);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w,
|
||||
const internal::Skip<T, _skip_serialization,
|
||||
_skip_deserialization>& _skip,
|
||||
const P& _parent) {
|
||||
if constexpr (_skip_serialization) {
|
||||
ParentType::add_null(_w, _parent);
|
||||
} else {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(
|
||||
_w, _skip.value(), _parent);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
77
build-config/reflect-cpp/include/rfl/parsing/Parser_span.hpp
Normal file
77
build-config/reflect-cpp/include/rfl/parsing/Parser_span.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef RFL_PARSING_PARSER_SPAN_HPP_
|
||||
#define RFL_PARSING_PARSER_SPAN_HPP_
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::span<T>>
|
||||
struct Parser<R, W, std::span<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<std::span<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if constexpr (!ProcessorsType::allow_raw_ptrs_) {
|
||||
static_assert(
|
||||
always_false_v<R>,
|
||||
"Reading into std::span is dangerous and "
|
||||
"therefore unsupported. "
|
||||
"Please consider using std::vector instead or wrapping "
|
||||
"std::vector in rfl::Box or rfl::Ref."
|
||||
"If you absolutely must use std::span, "
|
||||
"you can pass the rfl::AllowRawPtrs processor. "
|
||||
"Please note that it is then YOUR responsibility "
|
||||
"to delete the allocated memory. Please also refer "
|
||||
"to the related documentation (in the section on processors).");
|
||||
return error("Unsupported.");
|
||||
} else {
|
||||
return Parser<R, W, std::vector<std::remove_cvref_t<T>>,
|
||||
ProcessorsType>::read(_r, _var)
|
||||
.and_then([](std::vector<T>&& _vec) -> Result<std::span<T>> {
|
||||
using Type = std::remove_cvref_t<T>;
|
||||
Type* data = new (std::nothrow) Type[_vec.size()];
|
||||
if (!data) {
|
||||
return error("Failed to allocate memory for std::span.");
|
||||
}
|
||||
for (size_t i = 0; i < _vec.size(); ++i) {
|
||||
data[i] = std::move(_vec[i]);
|
||||
}
|
||||
return std::span<T>(data, data + _vec.size());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::span<T>& _span, const P& _parent) {
|
||||
auto arr = ParentType::add_array(_w, _span.size(), _parent);
|
||||
const auto new_parent = typename ParentType::Array{&arr};
|
||||
for (const auto& v : _span) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, v,
|
||||
new_parent);
|
||||
}
|
||||
_w.end_array(&arr);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::vector<std::remove_cvref_t<T>>,
|
||||
ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef RFL_PARSING_PARSER_STRING_VIEW_HPP_
|
||||
#define RFL_PARSING_PARSER_STRING_VIEW_HPP_
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::string_view>
|
||||
struct Parser<R, W, std::string_view, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<std::string_view> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if constexpr (!ProcessorsType::allow_raw_ptrs_) {
|
||||
static_assert(
|
||||
always_false_v<R>,
|
||||
"Reading into std::string_view is dangerous and "
|
||||
"therefore unsupported. "
|
||||
"Please consider using std::string instead or wrapping "
|
||||
"std::string in rfl::Box or rfl::Ref."
|
||||
"If you absolutely must use std::string_view, "
|
||||
"you can pass the rfl::AllowRawPtrs processor. "
|
||||
"Please note that it is then YOUR responsibility "
|
||||
"to delete the allocated memory. Please also refer "
|
||||
"to the related documentation (in the section on processors).");
|
||||
return error("Unsupported.");
|
||||
} else {
|
||||
return Parser<R, W, std::string, ProcessorsType>::read(_r, _var)
|
||||
.transform([](std::string&& str) {
|
||||
char* data =
|
||||
new char[str.size() + 1]; // +1 for the null terminator
|
||||
std::memcpy(data, str.data(), str.size() + 1);
|
||||
return std::string_view(data, str.size());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::string_view& _str, const P& _p) {
|
||||
Parser<R, W, std::string, ProcessorsType>::write(_w, std::string(_str), _p);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::string, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,240 @@
|
||||
#ifndef RFL_PARSING_PARSER_TAGGED_UNION_HPP_
|
||||
#define RFL_PARSING_PARSER_TAGGED_UNION_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../TaggedUnion.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/strings/strings.hpp"
|
||||
#include "../named_tuple_t.hpp"
|
||||
#include "../visit.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "TaggedUnionWrapper.hpp"
|
||||
#include "is_tagged_union_wrapper.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "tagged_union_wrapper_no_ptr.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, internal::StringLiteral _discriminator,
|
||||
class... AlternativeTypes, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W,
|
||||
TaggedUnion<_discriminator, AlternativeTypes...>>
|
||||
struct Parser<R, W, TaggedUnion<_discriminator, AlternativeTypes...>,
|
||||
ProcessorsType> {
|
||||
using ResultType = Result<TaggedUnion<_discriminator, AlternativeTypes...>>;
|
||||
|
||||
public:
|
||||
using InputObjectType = typename R::InputObjectType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
constexpr static bool no_field_names_ = ProcessorsType::no_field_names_;
|
||||
|
||||
using InputObjectOrArrayType =
|
||||
std::conditional_t<no_field_names_, typename R::InputArrayType,
|
||||
typename R::InputObjectType>;
|
||||
|
||||
static ResultType read(const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
return Parser<R, W, Variant<AlternativeTypes...>, ProcessorsType>::read(
|
||||
_r, _var)
|
||||
.transform([](auto&& _variant) {
|
||||
return TaggedUnion<_discriminator, AlternativeTypes...>(
|
||||
std::move(_variant));
|
||||
});
|
||||
|
||||
} else {
|
||||
const auto get_disc =
|
||||
[&_r](InputObjectOrArrayType _obj_or_arr) -> Result<std::string> {
|
||||
return get_discriminator(_r, _obj_or_arr);
|
||||
};
|
||||
|
||||
const auto to_result =
|
||||
[&_r, _var](const std::string& _disc_value) -> ResultType {
|
||||
return find_matching_alternative(
|
||||
_r, _disc_value, _var,
|
||||
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
|
||||
};
|
||||
|
||||
if constexpr (no_field_names_) {
|
||||
return _r.to_array(_var).and_then(get_disc).and_then(to_result);
|
||||
} else {
|
||||
return _r.to_object(_var).and_then(get_disc).and_then(to_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(
|
||||
const W& _w,
|
||||
const TaggedUnion<_discriminator, AlternativeTypes...>& _tagged_union,
|
||||
const P& _parent) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
Parser<R, W, Variant<AlternativeTypes...>, ProcessorsType>::write(
|
||||
_w, _tagged_union.variant(), _parent);
|
||||
} else {
|
||||
rfl::visit([&](const auto& _val) { write_wrapped(_w, _val, _parent); },
|
||||
_tagged_union.variant());
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulReader<R> &&
|
||||
schemaful::IsSchemafulWriter<W>) {
|
||||
return Parser<R, W, Variant<AlternativeTypes...>,
|
||||
ProcessorsType>::to_schema(_definitions);
|
||||
} else {
|
||||
using VariantType = std::variant<std::invoke_result_t<
|
||||
decltype(wrap_if_necessary<AlternativeTypes>), AlternativeTypes>...>;
|
||||
return Parser<R, W, VariantType, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <int... _is>
|
||||
static ResultType find_matching_alternative(
|
||||
const R& _r, const std::string& _disc_value, const InputVarType& _var,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
using PossibleTags =
|
||||
possible_tags_t<TaggedUnion<_discriminator, AlternativeTypes...>>;
|
||||
static_assert(!PossibleTags::has_duplicates(),
|
||||
"Duplicate tags are not allowed inside tagged unions.");
|
||||
ResultType res = error("");
|
||||
bool match_found = false;
|
||||
(set_if_disc_value_matches<_is>(_r, _disc_value, _var, &res, &match_found),
|
||||
...);
|
||||
if (match_found) [[likely]] {
|
||||
return res;
|
||||
} else {
|
||||
const auto names = PossibleTags::names();
|
||||
std::stringstream stream;
|
||||
stream << "Could not parse tagged union, could not match "
|
||||
<< _discriminator.str() << " '" << _disc_value
|
||||
<< "'. The following tags are allowed: "
|
||||
<< internal::strings::join(", ", names);
|
||||
return error(stream.str());
|
||||
}
|
||||
}
|
||||
|
||||
template <int _i>
|
||||
static void set_if_disc_value_matches(const R& _r,
|
||||
const std::string& _disc_value,
|
||||
const InputVarType& _var,
|
||||
ResultType* _result,
|
||||
bool* _match_found) {
|
||||
using AlternativeType = std::remove_cvref_t<
|
||||
std::variant_alternative_t<_i, std::variant<AlternativeTypes...>>>;
|
||||
|
||||
if (!*_match_found && contains_disc_value<AlternativeType>(_disc_value)) {
|
||||
const auto get_fields = [](auto&& _val) -> AlternativeType {
|
||||
if constexpr (is_tagged_union_wrapper_v<decltype(_val)>) {
|
||||
return std::move(_val.fields());
|
||||
} else {
|
||||
return std::move(_val);
|
||||
}
|
||||
};
|
||||
|
||||
const auto to_tagged_union = [](auto&& _val) {
|
||||
return TaggedUnion<_discriminator, AlternativeTypes...>(
|
||||
std::move(_val));
|
||||
};
|
||||
|
||||
const auto embellish_error = [&](auto&& _e) {
|
||||
std::stringstream stream;
|
||||
stream << "Could not parse tagged union with "
|
||||
"discrimininator "
|
||||
<< _discriminator.str() << " '" << _disc_value
|
||||
<< "': " << _e.what();
|
||||
return Error(stream.str());
|
||||
};
|
||||
|
||||
if constexpr (no_field_names_) {
|
||||
using T = tagged_union_wrapper_no_ptr_t<std::invoke_result_t<
|
||||
decltype(wrap_if_necessary<AlternativeType>), AlternativeType>>;
|
||||
*_result = Parser<R, W, T, ProcessorsType>::read(_r, _var)
|
||||
.transform(get_fields)
|
||||
.transform(to_tagged_union)
|
||||
.transform_error(embellish_error);
|
||||
} else {
|
||||
*_result = Parser<R, W, AlternativeType, ProcessorsType>::read(_r, _var)
|
||||
.transform(to_tagged_union)
|
||||
.transform_error(embellish_error);
|
||||
}
|
||||
|
||||
*_match_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the discriminator from an object
|
||||
static Result<std::string> get_discriminator(
|
||||
const R& _r, const InputObjectOrArrayType& _obj_or_arr) noexcept {
|
||||
const auto to_type = [&_r](auto _var) {
|
||||
return _r.template to_basic_type<std::string>(_var);
|
||||
};
|
||||
|
||||
const auto embellish_error = [](const auto&) {
|
||||
std::stringstream stream;
|
||||
stream << "Could not parse tagged union: Could not find field '"
|
||||
<< _discriminator.str() << "' or type of field was not a string.";
|
||||
return Error(stream.str());
|
||||
};
|
||||
|
||||
if constexpr (no_field_names_) {
|
||||
return _r.get_field_from_array(0, _obj_or_arr)
|
||||
.and_then(to_type)
|
||||
.transform_error(embellish_error);
|
||||
} else {
|
||||
return _r.get_field_from_object(_discriminator.str(), _obj_or_arr)
|
||||
.and_then(to_type)
|
||||
.transform_error(embellish_error);
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the discriminating literal contains the value
|
||||
/// retrieved from the object.
|
||||
template <class T>
|
||||
static inline bool contains_disc_value(
|
||||
const std::string& _disc_value) noexcept {
|
||||
return internal::tag_t<_discriminator, T>::contains(_disc_value);
|
||||
}
|
||||
|
||||
/// Writes a wrapped version of the original object, which contains the tag.
|
||||
template <class T, class P>
|
||||
static void write_wrapped(const W& _w, const T& _val, const P& _parent) {
|
||||
const auto wrapped = wrap_if_necessary<T>(_val);
|
||||
Parser<R, W, std::remove_cvref_t<decltype(wrapped)>, ProcessorsType>::write(
|
||||
_w, wrapped, _parent);
|
||||
}
|
||||
|
||||
/// Generates a wrapped version of the original object, which contains the
|
||||
/// tag, if the object doesn't already contain the wrap.
|
||||
template <class T>
|
||||
static auto wrap_if_necessary(const T& _val) {
|
||||
if constexpr (named_tuple_t<T>::Names::template contains<
|
||||
_discriminator>()) {
|
||||
return _val;
|
||||
} else {
|
||||
const auto tag = internal::make_tag<_discriminator, T>(_val);
|
||||
using TagType = std::remove_cvref_t<decltype(tag)>;
|
||||
if constexpr (internal::has_fields<std::remove_cvref_t<T>>()) {
|
||||
using WrapperType =
|
||||
TaggedUnionWrapperWithFields<T, TagType, _discriminator>;
|
||||
return WrapperType{.tag = tag, .fields = &_val};
|
||||
} else {
|
||||
using WrapperType =
|
||||
TaggedUnionWrapperNoFields<T, TagType, _discriminator>;
|
||||
return WrapperType{.tag = tag, .fields = &_val};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef RFL_PARSING_PARSER_TUPLE_HPP_
|
||||
#define RFL_PARSING_PARSER_TUPLE_HPP_
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "Parser_base.hpp"
|
||||
#include "TupleParser.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class... Ts, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::tuple<Ts...>>
|
||||
struct Parser<R, W, std::tuple<Ts...>, ProcessorsType>
|
||||
: public TupleParser<R, W, /*_ignore_empty_containers=*/false,
|
||||
/*_all_required=*/ProcessorsType::all_required_,
|
||||
ProcessorsType, std::tuple<Ts...>> {
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef RFL_PARSING_PARSER_UNIQUE_PTR_HPP_
|
||||
#define RFL_PARSING_PARSER_UNIQUE_PTR_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "schemaful/UniquePtrReader.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::unique_ptr<T>>
|
||||
struct Parser<R, W, std::unique_ptr<T>, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<std::unique_ptr<T>> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using S = schemaful::UniquePtrReader<R, W, std::remove_cvref_t<T>,
|
||||
ProcessorsType>;
|
||||
const auto to_unique = [&](const auto& _u) -> Result<std::unique_ptr<T>> {
|
||||
return _r.template read_union<std::unique_ptr<T>, S>(_u);
|
||||
};
|
||||
return _r.to_union(_var).and_then(to_unique);
|
||||
} else {
|
||||
if (_r.is_empty(_var)) {
|
||||
return std::unique_ptr<T>();
|
||||
}
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r,
|
||||
_var)
|
||||
.transform([](T&& _t) { return std::make_unique<T>(std::move(_t)); });
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::unique_ptr<T>& _s,
|
||||
const P& _parent) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
auto u = ParentType::add_union(_w, _parent);
|
||||
using UnionType = typename ParentType::template Union<decltype(u)>;
|
||||
auto p =
|
||||
UnionType{.index_ = static_cast<size_t>(_s ? 0 : 1), .union_ = &u};
|
||||
if (_s) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_s, p);
|
||||
} else {
|
||||
ParentType::add_null(_w, p);
|
||||
}
|
||||
} else {
|
||||
if (_s) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, *_s,
|
||||
_parent);
|
||||
} else {
|
||||
ParentType::add_null(_w, _parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
using U = std::remove_cvref_t<T>;
|
||||
return schema::Type{schema::Type::Optional{Ref<schema::Type>::make(
|
||||
Parser<R, W, U, ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
244
build-config/reflect-cpp/include/rfl/parsing/Parser_variant.hpp
Normal file
244
build-config/reflect-cpp/include/rfl/parsing/Parser_variant.hpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#ifndef RFL_PARSING_PARSER_VARIANT_HPP_
|
||||
#define RFL_PARSING_PARSER_VARIANT_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#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 <class R, class W, class... AlternativeTypes, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::variant<AlternativeTypes...>>
|
||||
class Parser<R, W, std::variant<AlternativeTypes...>, ProcessorsType> {
|
||||
template <class T>
|
||||
using ptr_field_t =
|
||||
decltype(internal::to_ptr_field(std::declval<const T&>()));
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
public:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
static Result<std::variant<AlternativeTypes...>> read(
|
||||
const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using WrappedType = rfl::Variant<NamedTuple<AlternativeTypes>...>;
|
||||
return Parser<R, W, WrappedType, ProcessorsType>::read(_r, _var)
|
||||
.transform(
|
||||
[](auto&& _variant) -> std::variant<AlternativeTypes...> {
|
||||
return std::move(_variant).visit([](auto&& _named_tuple) {
|
||||
return std::variant<AlternativeTypes...>(std::move(
|
||||
std::move(_named_tuple).fields().template get<0>()));
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
const auto wrap = [](auto&& _v) {
|
||||
return std::variant<AlternativeTypes...>(std::move(_v));
|
||||
};
|
||||
const auto to_std_variant = [&](auto&& _v) {
|
||||
return rfl::visit(wrap, std::move(_v));
|
||||
};
|
||||
return FieldVariantParser<R, W, ProcessorsType,
|
||||
AlternativeTypes...>::read(_r, _var)
|
||||
.transform(to_std_variant);
|
||||
}
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using V =
|
||||
schemaful::VariantReader<R, W, std::variant<AlternativeTypes...>,
|
||||
ProcessorsType, AlternativeTypes...>;
|
||||
return _r.to_union(_var).and_then([&](const auto& _u) {
|
||||
return _r.template read_union<std::variant<AlternativeTypes...>, 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<AlternativeTypes, remove_namespaces>...>;
|
||||
const auto from_field_variant =
|
||||
[](auto&& _field) -> std::variant<AlternativeTypes...> {
|
||||
return std::move(_field.value());
|
||||
};
|
||||
return Parser<R, W, FieldVariantType, ProcessorsType>::read(_r, _var)
|
||||
.transform([&](FieldVariantType&& _f) {
|
||||
return _f.visit(from_field_variant);
|
||||
});
|
||||
|
||||
} else {
|
||||
std::optional<std::variant<AlternativeTypes...>> result;
|
||||
std::vector<Error> errors;
|
||||
errors.reserve(sizeof...(AlternativeTypes));
|
||||
read_variant(
|
||||
_r, _var, &result, &errors,
|
||||
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
|
||||
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 <class P>
|
||||
static void write(const W& _w,
|
||||
const std::variant<AlternativeTypes...>& _variant,
|
||||
const P& _parent) {
|
||||
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
using WrappedType = rfl::Variant<
|
||||
NamedTuple<Field<AlternativeTypes::name_,
|
||||
const typename AlternativeTypes::Type*>>...>;
|
||||
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<R, W, WrappedType, ProcessorsType>::write(
|
||||
_w, to_wrapped(_variant), _parent);
|
||||
|
||||
} else {
|
||||
const auto wrap = [](const auto& _v) {
|
||||
return rfl::Variant<ptr_field_t<AlternativeTypes>...>(
|
||||
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<AlternativeTypes>...>::write(_w,
|
||||
to_rfl_variant(_variant),
|
||||
_parent);
|
||||
}
|
||||
|
||||
} else if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
return std::visit(
|
||||
[&](const auto& _v) {
|
||||
using Type = std::remove_cvref_t<decltype(_v)>;
|
||||
auto u = ParentType::add_union(_w, _parent);
|
||||
using UnionType = typename ParentType::template Union<decltype(u)>;
|
||||
auto p = UnionType{.index_ = static_cast<size_t>(_variant.index()),
|
||||
.union_ = &u};
|
||||
Parser<R, W, Type, ProcessorsType>::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<VariantAlternativeWrapper<const AlternativeTypes*,
|
||||
remove_namespaces>...>;
|
||||
const auto to_field_variant =
|
||||
[]<class T>(const T& _t) -> FieldVariantType {
|
||||
return VariantAlternativeWrapper<const T*, remove_namespaces>(&_t);
|
||||
};
|
||||
Parser<R, W, FieldVariantType, ProcessorsType>::write(
|
||||
_w, std::visit(to_field_variant, _variant), _parent);
|
||||
|
||||
} else {
|
||||
const auto handle = [&](const auto& _v) {
|
||||
using Type = std::remove_cvref_t<decltype(_v)>;
|
||||
Parser<R, W, Type, ProcessorsType>::write(_w, _v, _parent);
|
||||
};
|
||||
return std::visit(handle, _variant);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
if constexpr (internal::all_fields<std::tuple<AlternativeTypes...>>()) {
|
||||
return FieldVariantParser<R, W, ProcessorsType,
|
||||
AlternativeTypes...>::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<AlternativeTypes, remove_namespaces>...>;
|
||||
return Parser<R, W, FieldVariantType, ProcessorsType>::to_schema(
|
||||
_definitions);
|
||||
|
||||
} else {
|
||||
std::vector<schema::Type> types;
|
||||
build_schema(
|
||||
_definitions, &types,
|
||||
std::make_integer_sequence<int, sizeof...(AlternativeTypes)>());
|
||||
return schema::Type{schema::Type::AnyOf{.types_ = std::move(types)}};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t _i>
|
||||
static void add_to_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
std::vector<schema::Type>* _types) noexcept {
|
||||
using U = std::remove_cvref_t<
|
||||
std::variant_alternative_t<_i, std::variant<AlternativeTypes...>>>;
|
||||
_types->push_back(Parser<R, W, U, ProcessorsType>::to_schema(_definitions));
|
||||
}
|
||||
|
||||
template <int... _is>
|
||||
static void build_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
std::vector<schema::Type>* _types,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
(add_to_schema<_is>(_definitions, _types), ...);
|
||||
}
|
||||
|
||||
template <int _i>
|
||||
static void read_one_alternative(
|
||||
const R& _r, const InputVarType& _var,
|
||||
std::optional<std::variant<AlternativeTypes...>>* _result,
|
||||
std::vector<Error>* _errors) noexcept {
|
||||
if (!*_result) {
|
||||
using AltType =
|
||||
std::remove_cvref_t<internal::nth_element_t<_i, AlternativeTypes...>>;
|
||||
auto res = Parser<R, W, AltType, ProcessorsType>::read(_r, _var);
|
||||
if (res) {
|
||||
_result->emplace(std::move(*res));
|
||||
} else {
|
||||
_errors->emplace_back(std::move(res.error()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int... _is>
|
||||
static void read_variant(
|
||||
const R& _r, const InputVarType& _var,
|
||||
std::optional<std::variant<AlternativeTypes...>>* _result,
|
||||
std::vector<Error>* _errors,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
(read_one_alternative<_is>(_r, _var, _result, _errors), ...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
#ifndef RFL_PARSING_PARSER_VECTOR_LIKE_HPP_
|
||||
#define RFL_PARSING_PARSER_VECTOR_LIKE_HPP_
|
||||
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "VectorParser.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::deque<T>>
|
||||
struct Parser<R, W, std::deque<T>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::deque<T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::forward_list<T>>
|
||||
struct Parser<R, W, std::forward_list<T>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::forward_list<T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::list<T>>
|
||||
struct Parser<R, W, std::list<T>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::list<T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class K, class V, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::map<K, V>>
|
||||
struct Parser<R, W, std::map<K, V>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::map<K, V>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class K, class V, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::multimap<K, V>>
|
||||
struct Parser<R, W, std::multimap<K, V>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::multimap<K, V>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::multiset<T>>
|
||||
struct Parser<R, W, std::multiset<T>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::multiset<T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::set<T>>
|
||||
struct Parser<R, W, std::set<T>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::set<T>, ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class K, class V, class Hash, class KeyEqual,
|
||||
class Allocator, class ProcessorsType>
|
||||
requires AreReaderAndWriter<
|
||||
R, W, std::unordered_map<K, V, Hash, KeyEqual, Allocator>>
|
||||
struct Parser<R, W, std::unordered_map<K, V, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType>
|
||||
: public VectorParser<R, W,
|
||||
std::unordered_map<K, V, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class Hash, class KeyEqual,
|
||||
class Allocator, class ProcessorsType>
|
||||
requires AreReaderAndWriter<
|
||||
R, W, std::unordered_multiset<T, Hash, KeyEqual, Allocator>>
|
||||
struct Parser<R, W, std::unordered_multiset<T, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType>
|
||||
: public VectorParser<R, W,
|
||||
std::unordered_multiset<T, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class K, class V, class Hash, class KeyEqual,
|
||||
class Allocator, class ProcessorsType>
|
||||
requires AreReaderAndWriter<
|
||||
R, W, std::unordered_multimap<K, V, Hash, KeyEqual, Allocator>>
|
||||
struct Parser<R, W, std::unordered_multimap<K, V, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType>
|
||||
: public VectorParser<R, W, std::unordered_multimap<K, V>, ProcessorsType> {
|
||||
};
|
||||
|
||||
template <class R, class W, class T, class Hash, class KeyEqual,
|
||||
class Allocator, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W,
|
||||
std::unordered_set<T, Hash, KeyEqual, Allocator>>
|
||||
struct Parser<R, W, std::unordered_set<T, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType>
|
||||
: public VectorParser<R, W,
|
||||
std::unordered_set<T, Hash, KeyEqual, Allocator>,
|
||||
ProcessorsType> {};
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::vector<T>>
|
||||
struct Parser<R, W, std::vector<T>, ProcessorsType>
|
||||
: public VectorParser<R, W, std::vector<T>, ProcessorsType> {};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef RFL_PARSING_PARSER_VECTORSRING_HPP_
|
||||
#define RFL_PARSING_PARSER_VECTORSRING_HPP_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Vectorstring.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, Vectorstring>
|
||||
struct Parser<R, W, Vectorstring, ProcessorsType> {
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<Vectorstring> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
return _r.template to_basic_type<Vectorstring>(_var);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const Vectorstring& _b, const P& _parent) {
|
||||
ParentType::add_value(_w, _b, _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
[[maybe_unused]] std::map<std::string, schema::Type>* _definitions) {
|
||||
return schema::Type{schema::Type::Vectorstring{}};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef RFL_PARSING_PARSER_WSTRING_HPP_
|
||||
#define RFL_PARSING_PARSER_WSTRING_HPP_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class R, class W, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, std::wstring>
|
||||
struct Parser<R, W, std::wstring, ProcessorsType> {
|
||||
public:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<std::wstring> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if (_r.is_empty(_var)) {
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
auto inStr = Parser<R, W, std::string, ProcessorsType>::read(_r, _var);
|
||||
if (!inStr) {
|
||||
return Result<std::wstring>(error(inStr.error()));
|
||||
}
|
||||
// if (auto err = inStr.error(); err.has_value()) {
|
||||
// return Result<std::wstring>(err.value());
|
||||
// }
|
||||
|
||||
std::mbstate_t state = std::mbstate_t();
|
||||
auto val = inStr.value();
|
||||
|
||||
std::wstring outStr(val.size() * 2, L'\0');
|
||||
|
||||
// Explicitly set the size so we don't empty it when we truncate
|
||||
outStr.resize(val.size() * 2);
|
||||
|
||||
auto* ptr = val.c_str();
|
||||
|
||||
// Add 1 for null terminator
|
||||
auto len = std::mbsrtowcs(outStr.data(), &ptr, val.size(), &state);
|
||||
outStr.resize(len); // Truncate the extra bytes
|
||||
|
||||
return Result<std::wstring>(outStr);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const std::wstring& _str, const P& _parent) {
|
||||
if (_str.empty()) {
|
||||
ParentType::add_value(_w, std::string(), _parent);
|
||||
return;
|
||||
}
|
||||
|
||||
std::mbstate_t state = std::mbstate_t();
|
||||
std::string outStr(_str.size(), '\0');
|
||||
outStr.resize(_str.size());
|
||||
|
||||
auto* ptr = _str.c_str();
|
||||
auto len = std::wcsrtombs(outStr.data(), &ptr, _str.size(), &state);
|
||||
outStr.resize(len);
|
||||
|
||||
ParentType::add_value(_w, outStr, _parent);
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
return Parser<R, W, std::string, ProcessorsType>::to_schema(_definitions);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef RFL_PARSING_SUPPORTSTAGGEDUNIONS_HPP_
|
||||
#define RFL_PARSING_SUPPORTSTAGGEDUNIONS_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <string>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../internal/is_basic_type.hpp"
|
||||
#include "../internal/wrap_in_rfl_array_t.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R>
|
||||
concept SupportsTaggedUnions = requires(
|
||||
R r, std::string name, typename R::InputArrayType arr,
|
||||
typename R::InputObjectType obj, typename R::InputVarType var, size_t idx) {
|
||||
/// Retrieves a particular field from an array.
|
||||
{
|
||||
r.get_field_from_array(idx, arr)
|
||||
} -> std::same_as<rfl::Result<typename R::InputVarType>>;
|
||||
|
||||
/// Retrieves a particular field from an object.
|
||||
{
|
||||
r.get_field_from_object(name, obj)
|
||||
} -> std::same_as<rfl::Result<typename R::InputVarType>>;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef RFL_PARSING_TAGGEDUNIONWRAPPER_HPP_
|
||||
#define RFL_PARSING_TAGGEDUNIONWRAPPER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Field.hpp"
|
||||
#include "../Flatten.hpp"
|
||||
#include "../Rename.hpp"
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T, class TagType, internal::StringLiteral _discriminator,
|
||||
bool _as_const_pointer = true>
|
||||
struct TaggedUnionWrapperNoFields {
|
||||
using Type = std::remove_cvref_t<T>;
|
||||
using FlattenedType =
|
||||
std::conditional_t<_as_const_pointer, const Type*, Type>;
|
||||
rfl::Rename<_discriminator, TagType> tag;
|
||||
rfl::Flatten<FlattenedType> fields;
|
||||
};
|
||||
|
||||
template <class T, class TagType, internal::StringLiteral _discriminator,
|
||||
bool _as_const_pointer = true>
|
||||
struct TaggedUnionWrapperWithFields {
|
||||
using Type = T;
|
||||
using FlattenedType =
|
||||
std::conditional_t<_as_const_pointer, const Type*, Type>;
|
||||
rfl::Field<_discriminator, TagType> tag;
|
||||
rfl::Flatten<FlattenedType> fields;
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
133
build-config/reflect-cpp/include/rfl/parsing/TupleParser.hpp
Normal file
133
build-config/reflect-cpp/include/rfl/parsing/TupleParser.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#ifndef RFL_PARSING_TUPLEPARSER_HPP_
|
||||
#define RFL_PARSING_TUPLEPARSER_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Tuple.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "../internal/nth_element_t.hpp"
|
||||
#include "../internal/ptr_cast.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "TupleReader.hpp"
|
||||
#include "call_destructors_on_tuple_where_necessary.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
#include "schemaful/IsSchemafulWriter.hpp"
|
||||
#include "schemaful/tuple_to_named_tuple.hpp"
|
||||
#include "schemaful/tuple_to_named_tuple_t.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, bool _ignore_empty_containers, bool _all_required,
|
||||
class ProcessorsType, class TupleType>
|
||||
requires AreReaderAndWriter<R, W, TupleType>
|
||||
struct TupleParser {
|
||||
public:
|
||||
using InputArrayType = typename R::InputArrayType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
static Result<TupleType> read(const R& _r,
|
||||
const InputVarType& _var) noexcept {
|
||||
if constexpr (schemaful::IsSchemafulReader<R>) {
|
||||
using NamedTupleType = schemaful::tuple_to_named_tuple_t<TupleType>;
|
||||
const auto to_tuple = [](auto&& _named_tuple) {
|
||||
return [&]<int... _is>(std::integer_sequence<int, _is...>) {
|
||||
return TupleType(std::move(rfl::get<_is>(_named_tuple))...);
|
||||
}(std::make_integer_sequence<int, NamedTupleType::size()>());
|
||||
};
|
||||
return Parser<R, W, NamedTupleType, ProcessorsType>::read(_r, _var)
|
||||
.transform(to_tuple);
|
||||
|
||||
} else {
|
||||
const auto parse = [&](const InputArrayType& _arr) -> Result<TupleType> {
|
||||
alignas(TupleType) unsigned char buf[sizeof(TupleType)]{};
|
||||
auto ptr = internal::ptr_cast<TupleType*>(&buf);
|
||||
const auto tuple_reader =
|
||||
TupleReader<R, W, TupleType, _ignore_empty_containers,
|
||||
_all_required, ProcessorsType>(&_r, ptr);
|
||||
auto err = _r.read_array(tuple_reader, _arr);
|
||||
if (err) {
|
||||
call_destructors_on_tuple_where_necessary(tuple_reader.num_set(),
|
||||
ptr);
|
||||
return error(*err);
|
||||
}
|
||||
err = tuple_reader.handle_missing_fields();
|
||||
if (err) {
|
||||
call_destructors_on_tuple_where_necessary(tuple_reader.num_set(),
|
||||
ptr);
|
||||
return error(*err);
|
||||
}
|
||||
auto res = Result<TupleType>(std::move(*ptr));
|
||||
call_destructors_on_tuple_where_necessary(tuple_reader.num_set(), ptr);
|
||||
return res;
|
||||
};
|
||||
|
||||
return _r.to_array(_var).and_then(parse);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const TupleType& _tup, const P& _parent) {
|
||||
if constexpr (schemaful::IsSchemafulWriter<W>) {
|
||||
const auto named_tuple = schemaful::tuple_to_named_tuple(_tup);
|
||||
Parser<R, W, std::remove_cvref_t<decltype(named_tuple)>,
|
||||
ProcessorsType>::write(_w, named_tuple, _parent);
|
||||
|
||||
} else {
|
||||
constexpr auto size = rfl::tuple_size_v<TupleType>;
|
||||
auto arr = ParentType::add_array(_w, size, _parent);
|
||||
const auto new_parent = typename ParentType::Array{&arr};
|
||||
to_array(_w, _tup, new_parent, std::make_integer_sequence<int, size>());
|
||||
_w.end_array(&arr);
|
||||
}
|
||||
}
|
||||
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
std::vector<schema::Type> types;
|
||||
build_schema(
|
||||
_definitions, &types,
|
||||
std::make_integer_sequence<int, rfl::tuple_size_v<TupleType>>());
|
||||
return schema::Type{schema::Type::Tuple{.types_ = std::move(types)}};
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t _i>
|
||||
static void add_to_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
std::vector<schema::Type>* _types) noexcept {
|
||||
using U = std::remove_cvref_t<rfl::tuple_element_t<_i, TupleType>>;
|
||||
_types->push_back(Parser<R, W, U, ProcessorsType>::to_schema(_definitions));
|
||||
}
|
||||
|
||||
template <int... _is>
|
||||
static void build_schema(std::map<std::string, schema::Type>* _definitions,
|
||||
std::vector<schema::Type>* _types,
|
||||
std::integer_sequence<int, _is...>) noexcept {
|
||||
(add_to_schema<_is>(_definitions, _types), ...);
|
||||
}
|
||||
|
||||
template <int _i, class P>
|
||||
static void add_to_array(const W& _w, const TupleType& _tup,
|
||||
const P& _parent) {
|
||||
using NewFieldType =
|
||||
std::remove_cvref_t<rfl::tuple_element_t<_i, TupleType>>;
|
||||
Parser<R, W, NewFieldType, ProcessorsType>::write(_w, rfl::get<_i>(_tup),
|
||||
_parent);
|
||||
}
|
||||
|
||||
template <int... _is, class P>
|
||||
static void to_array(const W& _w, const TupleType& _tup, const P& _parent,
|
||||
std::integer_sequence<int, _is...>) {
|
||||
(add_to_array<_is>(_w, _tup, _parent), ...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
127
build-config/reflect-cpp/include/rfl/parsing/TupleReader.hpp
Normal file
127
build-config/reflect-cpp/include/rfl/parsing/TupleReader.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef RFL_PARSING_TUPLEREADER_HPP_
|
||||
#define RFL_PARSING_TUPLEREADER_HPP_
|
||||
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#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 R, class W, class TupleType, bool _ignore_empty_containers,
|
||||
bool _all_required, class ProcessorsType>
|
||||
class TupleReader {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
static constexpr size_t size_ = rfl::tuple_size_v<TupleType>;
|
||||
|
||||
public:
|
||||
TupleReader(const R* _r, TupleType* _tuple)
|
||||
: num_set_(0), r_(_r), tuple_(_tuple) {}
|
||||
|
||||
~TupleReader() = default;
|
||||
|
||||
std::optional<Error> handle_missing_fields() const {
|
||||
std::optional<Error> err;
|
||||
if (num_set_ < size_) {
|
||||
handle_missing_fields_impl(&err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t num_set() const { return num_set_; }
|
||||
|
||||
std::optional<Error> read(const InputVarType& _var) const {
|
||||
std::optional<Error> err;
|
||||
read_impl(_var, &err);
|
||||
return err;
|
||||
}
|
||||
|
||||
private:
|
||||
template <int _i = 0>
|
||||
void handle_missing_fields_impl(std::optional<Error>* _err) const noexcept {
|
||||
if constexpr (_i < size_) {
|
||||
if (num_set_ == _i) {
|
||||
using CurrentType =
|
||||
std::remove_cvref_t<rfl::tuple_element_t<_i, TupleType>>;
|
||||
|
||||
if constexpr (_all_required ||
|
||||
is_required<CurrentType, _ignore_empty_containers>()) {
|
||||
*_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 <size_t _i = 0>
|
||||
void read_impl(const InputVarType& _var, std::optional<Error>* _err) const {
|
||||
if constexpr (_i < size_) {
|
||||
if (num_set_ == _i) {
|
||||
using CurrentType =
|
||||
std::remove_cvref_t<rfl::tuple_element_t<_i, TupleType>>;
|
||||
|
||||
auto res = Parser<R, W, CurrentType, ProcessorsType>::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 <class Target, class Source>
|
||||
void move_to(Target* _t, Source* _s) const {
|
||||
if constexpr (std::is_const_v<Target>) {
|
||||
return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
|
||||
} else if constexpr (!internal::is_array_v<Source> &&
|
||||
!std::is_array_v<Target>) {
|
||||
::new (_t) Target(std::move(*_s));
|
||||
} else if constexpr (internal::is_array_v<Source>) {
|
||||
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
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef RFL_PARSING_VARIANTALTERNATIVEWRAPPER_HPP_
|
||||
#define RFL_PARSING_VARIANTALTERNATIVEWRAPPER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Field.hpp"
|
||||
#include "../Literal.hpp"
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
#include "../internal/get_type_name.hpp"
|
||||
#include "../internal/has_tag_v.hpp"
|
||||
#include "../internal/remove_namespaces.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
namespace vaw {
|
||||
|
||||
template <class T>
|
||||
struct GetName {};
|
||||
|
||||
template <internal::StringLiteral _name>
|
||||
struct GetName<Literal<_name>> {
|
||||
constexpr static internal::StringLiteral name_ = _name;
|
||||
};
|
||||
|
||||
template <class T, bool _remove_namespaces = true>
|
||||
consteval auto make_tag() {
|
||||
if constexpr (internal::has_tag_v<T>) {
|
||||
return typename T::Tag();
|
||||
} else if constexpr (std::is_same_v<std::remove_cvref_t<T>, std::string>) {
|
||||
return Literal<"std::string">();
|
||||
} else if constexpr (_remove_namespaces) {
|
||||
return Literal<
|
||||
internal::remove_namespaces<internal::get_type_name<T>()>()>();
|
||||
} else {
|
||||
return Literal<internal::get_type_name<T>()>();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, bool _remove_namespaces = true>
|
||||
using tag_t = std::invoke_result_t<
|
||||
decltype(make_tag<std::remove_cvref_t<std::remove_pointer_t<T>>, _remove_namespaces>)>;
|
||||
|
||||
} // namespace vaw
|
||||
|
||||
template <class T, bool _remove_namespaces = true>
|
||||
using VariantAlternativeWrapper = Field<vaw::GetName<vaw::tag_t<T, _remove_namespaces>>::name_, T>;
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
128
build-config/reflect-cpp/include/rfl/parsing/VectorParser.hpp
Normal file
128
build-config/reflect-cpp/include/rfl/parsing/VectorParser.hpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#ifndef RFL_PARSING_VECTORPARSER_HPP_
|
||||
#define RFL_PARSING_VECTORPARSER_HPP_
|
||||
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Bytestring.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../Vectorstring.hpp"
|
||||
#include "../always_false.hpp"
|
||||
#include "MapParser.hpp"
|
||||
#include "Parent.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "VectorReader.hpp"
|
||||
#include "is_forward_list.hpp"
|
||||
#include "is_map_like.hpp"
|
||||
#include "is_map_like_not_multimap.hpp"
|
||||
#include "is_set_like.hpp"
|
||||
#include "schema/Type.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
/// This can be used for data structures that would be expressed as array in
|
||||
/// serialized format (std::vector, std::set, std::deque, ...),
|
||||
/// but also includes map-like types, when the key is not of type
|
||||
/// std::string.
|
||||
template <class R, class W, class VecType, class ProcessorsType>
|
||||
requires AreReaderAndWriter<R, W, VecType>
|
||||
struct VectorParser {
|
||||
public:
|
||||
using InputArrayType = typename R::InputArrayType;
|
||||
using InputVarType = typename R::InputVarType;
|
||||
|
||||
using ParentType = Parent<W>;
|
||||
|
||||
using T = typename VecType::value_type;
|
||||
|
||||
static_assert(!std::is_same_v<std::remove_cvref_t<VecType>, Bytestring>,
|
||||
"Cannot be a bytestring.");
|
||||
static_assert(!std::is_same_v<std::remove_cvref_t<VecType>, Vectorstring>,
|
||||
"Cannot be a vectorstring.");
|
||||
|
||||
static Result<VecType> read(const R& _r, const InputVarType& _var) noexcept {
|
||||
if constexpr (treat_as_map()) {
|
||||
return MapParser<R, W, VecType, ProcessorsType>::read(_r, _var);
|
||||
} else if constexpr (is_forward_list<VecType>()) {
|
||||
const auto to_forward_list = [](auto&& vec) -> std::forward_list<T> {
|
||||
std::forward_list<T> list;
|
||||
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
|
||||
list.emplace_front(std::move(*it));
|
||||
}
|
||||
return list;
|
||||
};
|
||||
return Parser<R, W, std::vector<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform(to_forward_list);
|
||||
} else {
|
||||
const auto parse = [&](const InputArrayType& _arr) -> Result<VecType> {
|
||||
VecType vec;
|
||||
auto vector_reader =
|
||||
VectorReader<R, W, VecType, ProcessorsType>(&_r, &vec);
|
||||
const auto err = _r.read_array(vector_reader, _arr);
|
||||
if (err) {
|
||||
return error(*err);
|
||||
}
|
||||
return vec;
|
||||
};
|
||||
return _r.to_array(_var).and_then(parse);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
static void write(const W& _w, const VecType& _vec, const P& _parent) {
|
||||
if constexpr (treat_as_map()) {
|
||||
MapParser<R, W, VecType, ProcessorsType>::write(_w, _vec, _parent);
|
||||
} else {
|
||||
auto arr = ParentType::add_array(
|
||||
_w, std::distance(_vec.begin(), _vec.end()), _parent);
|
||||
const auto new_parent = typename ParentType::Array{&arr};
|
||||
for (const auto& v : _vec) {
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::write(_w, v,
|
||||
new_parent);
|
||||
}
|
||||
_w.end_array(&arr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a schema for the underlying type.
|
||||
static schema::Type to_schema(
|
||||
std::map<std::string, schema::Type>* _definitions) {
|
||||
if constexpr (treat_as_map()) {
|
||||
return MapParser<R, W, VecType, ProcessorsType>::to_schema(_definitions);
|
||||
} else {
|
||||
using Type = schema::Type;
|
||||
return Type{Type::TypedArray{
|
||||
.type_ = Ref<Type>::make(
|
||||
Parser<R, W, T, ProcessorsType>::to_schema(_definitions))}};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr bool treat_as_map() {
|
||||
if constexpr (is_map_like_not_multimap<VecType>()) {
|
||||
if constexpr (internal::has_reflection_type_v<typename T::first_type>) {
|
||||
using U = std::remove_cvref_t<typename T::first_type::ReflectionType>;
|
||||
return std::is_same<U, std::string>() || std::is_integral_v<U> ||
|
||||
std::is_floating_point_v<U>;
|
||||
|
||||
// We do not need std::string here, it is already caught by the template
|
||||
// specialization.
|
||||
} else if constexpr (std::is_integral_v<typename T::first_type> ||
|
||||
std::is_floating_point_v<typename T::first_type>) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef RFL_PARSING_VECTORREADER_HPP_
|
||||
#define RFL_PARSING_VECTORREADER_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "is_map_like.hpp"
|
||||
#include "is_set_like.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class VecType, class ProcessorsType>
|
||||
class VectorReader {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
using T = typename VecType::value_type;
|
||||
|
||||
public:
|
||||
VectorReader(const R* _r, VecType* _vec) : r_(_r), vec_(_vec) {}
|
||||
|
||||
~VectorReader() = default;
|
||||
|
||||
std::optional<Error> read(const InputVarType& _var) const {
|
||||
const auto parse = [this](const InputVarType& _var) {
|
||||
if constexpr (is_map_like_v<VecType>) {
|
||||
return get_pair(_var);
|
||||
} else {
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(*r_,
|
||||
_var);
|
||||
}
|
||||
};
|
||||
|
||||
const auto insert = [this](auto&& _var) -> std::optional<Error> {
|
||||
if constexpr (is_map_like_v<VecType> || is_set_like_v<VecType>) {
|
||||
vec_->insert(std::move(_var));
|
||||
} else {
|
||||
vec_->emplace_back(std::move(_var));
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
auto res = parse(_var).transform(insert);
|
||||
if (res) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return res.error();
|
||||
}
|
||||
// return parse(_var).transform(insert).error();
|
||||
}
|
||||
|
||||
private:
|
||||
auto get_pair(const auto& _var) const {
|
||||
using K = std::remove_cvref_t<typename T::first_type>;
|
||||
using V = std::remove_cvref_t<typename T::second_type>;
|
||||
return Parser<R, W, std::remove_cvref_t<std::pair<K, V>>,
|
||||
ProcessorsType>::read(*r_, _var);
|
||||
}
|
||||
|
||||
private:
|
||||
/// The underlying reader.
|
||||
const R* r_;
|
||||
|
||||
/// The underlying vector.
|
||||
VecType* vec_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
192
build-config/reflect-cpp/include/rfl/parsing/ViewReader.hpp
Normal file
192
build-config/reflect-cpp/include/rfl/parsing/ViewReader.hpp
Normal file
@@ -0,0 +1,192 @@
|
||||
#ifndef RFL_PARSING_VIEWREADER_HPP_
|
||||
#define RFL_PARSING_VIEWREADER_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Tuple.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
#include "schemaful/IsSchemafulReader.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ViewType, class ProcessorsType>
|
||||
class ViewReader {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
static constexpr size_t size_ = ViewType::size();
|
||||
|
||||
public:
|
||||
ViewReader(const R* _r, ViewType* _view, std::array<bool, size_>* _found,
|
||||
std::array<bool, size_>* _set, std::vector<Error>* _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<int, size_>());
|
||||
}
|
||||
|
||||
/// 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<int, size_>());
|
||||
}
|
||||
|
||||
private:
|
||||
template <int i, class FieldType>
|
||||
static bool is_matching(const int _current_index) {
|
||||
return _current_index == i;
|
||||
}
|
||||
|
||||
template <int i, class FieldType>
|
||||
static bool is_matching(const std::string_view& _current_name) {
|
||||
return _current_name == FieldType::name();
|
||||
}
|
||||
|
||||
template <int i>
|
||||
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<i, typename ViewType::Fields>;
|
||||
using OriginalType = typename FieldType::Type;
|
||||
using T =
|
||||
std::remove_cvref_t<std::remove_pointer_t<typename FieldType::Type>>;
|
||||
constexpr auto name = FieldType::name();
|
||||
if (!(*_already_assigned) && !std::get<i>(*_found) &&
|
||||
is_matching<i, FieldType>(_current_name_or_index)) {
|
||||
std::get<i>(*_found) = true;
|
||||
*_already_assigned = true;
|
||||
auto res = Parser<R, W, T, ProcessorsType>::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<OriginalType>) {
|
||||
move_to(rfl::get<i>(*_view), &(*res));
|
||||
} else {
|
||||
rfl::get<i>(*_view) = std::move(*res);
|
||||
}
|
||||
std::get<i>(*_set) = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <int _pos>
|
||||
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<std::remove_pointer_t<decltype(extra_fields)>>;
|
||||
using T = std::remove_cvref_t<
|
||||
std::remove_pointer_t<typename ExtraFieldsType::Type>>;
|
||||
if (!std::get<_pos>(*_set)) {
|
||||
::new (extra_fields) ExtraFieldsType();
|
||||
std::get<_pos>(*_set) = true;
|
||||
std::get<_pos>(*_found) = true;
|
||||
}
|
||||
auto res = Parser<R, W, T, ProcessorsType>::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 <int... is>
|
||||
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<int, is...>) {
|
||||
bool already_assigned = false;
|
||||
|
||||
(assign_if_field_matches<is>(_r, _current_name_or_index, _var, _view,
|
||||
_errors, _found, _set, &already_assigned),
|
||||
...);
|
||||
|
||||
if constexpr (ViewType::pos_extra_fields() != -1) {
|
||||
static_assert(!schemaful::IsSchemafulReader<R>,
|
||||
"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<pos>(_r, _current_name_or_index, _var, _view,
|
||||
_errors, _found, _set);
|
||||
}
|
||||
} else if constexpr (ProcessorsType::no_extra_fields_) {
|
||||
static_assert(
|
||||
!schemaful::IsSchemafulReader<R>,
|
||||
"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 <class Target, class Source>
|
||||
static void move_to(Target* _t, Source* _s) {
|
||||
if constexpr (std::is_const_v<Target>) {
|
||||
return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
|
||||
} else if constexpr (!rfl::internal::is_array_v<Source> &&
|
||||
!std::is_array_v<Target>) {
|
||||
::new (_t) Target(std::move(*_s));
|
||||
} else if constexpr (rfl::internal::is_array_v<Source>) {
|
||||
static_assert(std::is_array_v<Target>,
|
||||
"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<bool, size_>* found_;
|
||||
|
||||
/// Indicates that a certain field has been successfully set - necessary,
|
||||
/// because we have to trigger the destructors manually.
|
||||
std::array<bool, size_>* set_;
|
||||
|
||||
/// Collects any errors we may have come across.
|
||||
std::vector<Error>* errors_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,160 @@
|
||||
#ifndef RFL_PARSING_VIEWREADERWITHDEFAULT_HPP_
|
||||
#define RFL_PARSING_VIEWREADERWITHDEFAULT_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../Ref.hpp"
|
||||
#include "../Result.hpp"
|
||||
#include "../Tuple.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ViewType, class ProcessorsType>
|
||||
class ViewReaderWithDefault {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
static constexpr size_t size_ = ViewType::size();
|
||||
|
||||
public:
|
||||
ViewReaderWithDefault(const R* _r, ViewType* _view,
|
||||
std::vector<Error>* _errors)
|
||||
: r_(_r), view_(_view), errors_(_errors) {
|
||||
found_->fill(false);
|
||||
}
|
||||
|
||||
~ViewReaderWithDefault() = default;
|
||||
|
||||
const std::array<bool, size_>& found() const { return *found_; }
|
||||
|
||||
/// 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_.get(),
|
||||
std::make_integer_sequence<int, size_>());
|
||||
}
|
||||
|
||||
private:
|
||||
template <int i>
|
||||
static void assign_if_field_matches(const R& _r,
|
||||
const std::string_view& _current_name,
|
||||
const auto& _var, auto* _view,
|
||||
auto* _errors, auto* _found,
|
||||
bool* _already_assigned) {
|
||||
using FieldType = tuple_element_t<i, typename ViewType::Fields>;
|
||||
using OriginalType = typename FieldType::Type;
|
||||
using T =
|
||||
std::remove_cvref_t<std::remove_pointer_t<typename FieldType::Type>>;
|
||||
constexpr auto name = FieldType::name();
|
||||
if (!(*_already_assigned) && !std::get<i>(*_found) &&
|
||||
_current_name == name) {
|
||||
std::get<i>(*_found) = true;
|
||||
*_already_assigned = true;
|
||||
auto res = Parser<R, W, T, ProcessorsType>::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<OriginalType>) {
|
||||
move_to(rfl::get<i>(*_view), &(*res));
|
||||
} else {
|
||||
rfl::get<i>(*_view) = std::move(*res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int _pos>
|
||||
static void assign_to_extra_fields(const R& _r,
|
||||
const std::string_view& _current_name,
|
||||
const auto& _var, auto* _view,
|
||||
auto* _errors) {
|
||||
auto* extra_fields = _view->template get<_pos>();
|
||||
using ExtraFieldsType =
|
||||
std::remove_cvref_t<std::remove_pointer_t<decltype(extra_fields)>>;
|
||||
using T = std::remove_cvref_t<
|
||||
std::remove_pointer_t<typename ExtraFieldsType::Type>>;
|
||||
auto res = Parser<R, W, T, ProcessorsType>::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 <int... is>
|
||||
static void assign_to_matching_field(const R& _r,
|
||||
const std::string_view& _current_name,
|
||||
const auto& _var, auto* _view,
|
||||
auto* _errors, auto* _found,
|
||||
std::integer_sequence<int, is...>) {
|
||||
bool already_assigned = false;
|
||||
|
||||
(assign_if_field_matches<is>(_r, _current_name, _var, _view, _errors,
|
||||
_found, &already_assigned),
|
||||
...);
|
||||
|
||||
if constexpr (ViewType::pos_extra_fields() != -1) {
|
||||
constexpr int pos = ViewType::pos_extra_fields();
|
||||
if (!already_assigned) {
|
||||
assign_to_extra_fields<pos>(_r, _current_name, _var, _view, _errors);
|
||||
}
|
||||
} else if constexpr (ProcessorsType::no_extra_fields_) {
|
||||
if (!already_assigned) {
|
||||
std::stringstream stream;
|
||||
stream << "Value named '" << std::string(_current_name)
|
||||
<< "' not used. Remove the rfl::NoExtraFields processor or add "
|
||||
"rfl::ExtraFields to avoid this error message.";
|
||||
_errors->emplace_back(Error(stream.str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Target, class Source>
|
||||
static void move_to(Target* _t, Source* _s) {
|
||||
if constexpr (std::is_const_v<Target>) {
|
||||
return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
|
||||
} else if constexpr (!rfl::internal::is_array_v<Source> &&
|
||||
!std::is_array_v<Target>) {
|
||||
*_t = Target(std::move(*_s));
|
||||
} else if constexpr (rfl::internal::is_array_v<Source>) {
|
||||
static_assert(std::is_array_v<Target>,
|
||||
"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.
|
||||
rfl::Ref<std::array<bool, size_>> found_;
|
||||
|
||||
/// Collects any errors we may have come across.
|
||||
std::vector<Error>* errors_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
#ifndef RFL_PARSING_VIEWREADERWITHDEFAULTANDSTRIPPEDFIELDNAMES_HPP_
|
||||
#define RFL_PARSING_VIEWREADERWITHDEFAULTANDSTRIPPEDFIELDNAMES_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Tuple.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ViewType, class ProcessorsType>
|
||||
class ViewReaderWithDefaultAndStrippedFieldNames {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
static constexpr size_t size_ = ViewType::size();
|
||||
|
||||
public:
|
||||
ViewReaderWithDefaultAndStrippedFieldNames(const R* _r, ViewType* _view,
|
||||
std::vector<Error>* _errors)
|
||||
: i_(0), r_(_r), view_(_view), errors_(_errors) {}
|
||||
|
||||
~ViewReaderWithDefaultAndStrippedFieldNames() = default;
|
||||
|
||||
std::array<bool, size_> found() const {
|
||||
std::array<bool, size_> f;
|
||||
std::fill(f.begin(), f.begin() + i_, true);
|
||||
std::fill(f.begin() + i_, f.end(), false);
|
||||
return f;
|
||||
}
|
||||
|
||||
/// Assigns the parsed version of _var to the field signified by i_, to be
|
||||
/// used when the field names are stripped.
|
||||
std::optional<Error> read(const InputVarType& _var) const {
|
||||
if (i_ == size_) {
|
||||
std::stringstream stream;
|
||||
stream << "Expected a maximum of " << std::to_string(size_)
|
||||
<< " fields, but got at least one more.";
|
||||
return Error(stream.str());
|
||||
}
|
||||
assign_to_field_i(*r_, _var, view_, errors_, i_,
|
||||
std::make_integer_sequence<int, size_>());
|
||||
++i_;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
template <int i>
|
||||
static void assign_if_field_is_field_i(const R& _r, const auto& _var,
|
||||
auto* _view, auto* _errors, int _i) {
|
||||
using FieldType = tuple_element_t<i, typename ViewType::Fields>;
|
||||
using OriginalType = typename FieldType::Type;
|
||||
using T =
|
||||
std::remove_cvref_t<std::remove_pointer_t<typename FieldType::Type>>;
|
||||
constexpr auto name = FieldType::name();
|
||||
if (_i == i) {
|
||||
auto res = Parser<R, W, T, ProcessorsType>::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<OriginalType>) {
|
||||
move_to(rfl::get<i>(*_view), &(*res));
|
||||
} else {
|
||||
rfl::get<i>(*_view) = std::move(*res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int... is>
|
||||
static void assign_to_field_i(const R& _r, const auto& _var, auto* _view,
|
||||
auto* _errors, int _i,
|
||||
std::integer_sequence<int, is...>) {
|
||||
(assign_if_field_is_field_i<is>(_r, _var, _view, _errors, _i), ...);
|
||||
}
|
||||
|
||||
// TODO: Unnecessary code duplication.
|
||||
template <class Target, class Source>
|
||||
static void move_to(Target* _t, Source* _s) {
|
||||
if constexpr (std::is_const_v<Target>) {
|
||||
return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
|
||||
} else if constexpr (!rfl::internal::is_array_v<Source> &&
|
||||
!std::is_array_v<Target>) {
|
||||
*_t = Target(std::move(*_s));
|
||||
} else if constexpr (rfl::internal::is_array_v<Source>) {
|
||||
static_assert(std::is_array_v<Target>,
|
||||
"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:
|
||||
/// Indicates the current field.
|
||||
mutable int i_;
|
||||
|
||||
/// The underlying reader.
|
||||
const R* r_;
|
||||
|
||||
/// The underlying view.
|
||||
ViewType* view_;
|
||||
|
||||
/// Collects any errors we may have come across.
|
||||
std::vector<Error>* errors_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef RFL_PARSING_VIEWREADERWITHSTRIPPEDFIELDNAMES_HPP_
|
||||
#define RFL_PARSING_VIEWREADERWITHSTRIPPEDFIELDNAMES_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../Tuple.hpp"
|
||||
#include "../internal/is_array.hpp"
|
||||
#include "Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
template <class R, class W, class ViewType, class ProcessorsType>
|
||||
class ViewReaderWithStrippedFieldNames {
|
||||
private:
|
||||
using InputVarType = typename R::InputVarType;
|
||||
static constexpr size_t size_ = ViewType::size();
|
||||
|
||||
public:
|
||||
ViewReaderWithStrippedFieldNames(const R* _r, ViewType* _view,
|
||||
std::array<bool, size_>* _found,
|
||||
std::array<bool, size_>* _set,
|
||||
std::vector<Error>* _errors)
|
||||
: i_(0),
|
||||
r_(_r),
|
||||
view_(_view),
|
||||
found_(_found),
|
||||
set_(_set),
|
||||
errors_(_errors) {}
|
||||
|
||||
~ViewReaderWithStrippedFieldNames() = default;
|
||||
|
||||
/// Assigns the parsed version of _var to the field signified by i_, to be
|
||||
/// used when the field names are stripped.
|
||||
std::optional<Error> read(const InputVarType& _var) const {
|
||||
if (i_ == size_) {
|
||||
std::stringstream stream;
|
||||
stream << "Expected a maximum of " << std::to_string(size_)
|
||||
<< " fields, but got at least one more.";
|
||||
return Error(stream.str());
|
||||
}
|
||||
assign_to_field_i(*r_, _var, view_, errors_, found_, set_, i_,
|
||||
std::make_integer_sequence<int, size_>());
|
||||
++i_;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
template <int i>
|
||||
static void assign_if_field_is_field_i(const R& _r, const auto& _var,
|
||||
auto* _view, auto* _errors,
|
||||
auto* _found, auto* _set, int _i) {
|
||||
using FieldType = tuple_element_t<i, typename ViewType::Fields>;
|
||||
using OriginalType = typename FieldType::Type;
|
||||
using T =
|
||||
std::remove_cvref_t<std::remove_pointer_t<typename FieldType::Type>>;
|
||||
constexpr auto name = FieldType::name();
|
||||
if (_i == i) {
|
||||
std::get<i>(*_found) = true;
|
||||
auto res = Parser<R, W, T, ProcessorsType>::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<OriginalType>) {
|
||||
move_to(rfl::get<i>(*_view), &(*res));
|
||||
} else {
|
||||
rfl::get<i>(*_view) = std::move(*res);
|
||||
}
|
||||
std::get<i>(*_set) = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <int... is>
|
||||
static void assign_to_field_i(const R& _r, const auto& _var, auto* _view,
|
||||
auto* _errors, auto* _found, auto* _set, int _i,
|
||||
std::integer_sequence<int, is...>) {
|
||||
(assign_if_field_is_field_i<is>(_r, _var, _view, _errors, _found, _set, _i),
|
||||
...);
|
||||
}
|
||||
|
||||
// TODO: Unnecessary code duplication.
|
||||
template <class Target, class Source>
|
||||
static void move_to(Target* _t, Source* _s) {
|
||||
if constexpr (std::is_const_v<Target>) {
|
||||
return move_to(const_cast<std::remove_const_t<Target>*>(_t), _s);
|
||||
} else if constexpr (!rfl::internal::is_array_v<Source> &&
|
||||
!std::is_array_v<Target>) {
|
||||
::new (_t) Target(std::move(*_s));
|
||||
} else if constexpr (rfl::internal::is_array_v<Source>) {
|
||||
static_assert(std::is_array_v<Target>,
|
||||
"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:
|
||||
/// Indicates the current field.
|
||||
mutable int i_;
|
||||
|
||||
/// The underlying reader.
|
||||
const R* r_;
|
||||
|
||||
/// The underlying view.
|
||||
ViewType* view_;
|
||||
|
||||
/// Indicates that a certain field has been found.
|
||||
std::array<bool, size_>* found_;
|
||||
|
||||
/// Indicates that a certain field has been successfully set - necessary,
|
||||
/// because we have to trigger the destructors manually.
|
||||
std::array<bool, size_>* set_;
|
||||
|
||||
/// Collects any errors we may have come across.
|
||||
std::vector<Error>* errors_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef RFL_PARSING_CALL_DESTRUCTORS_ON_ARRAY_WHERE_NECESSARY_HPP_
|
||||
#define RFL_PARSING_CALL_DESTRUCTORS_ON_ARRAY_WHERE_NECESSARY_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
#include "call_destructors_where_necessary.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
/// Because of the way we have allocated the fields, we need to manually
|
||||
/// trigger the destructors.
|
||||
|
||||
template <class T, size_t _size>
|
||||
void call_destructors_on_array_where_necessary(const size_t _num_set,
|
||||
std::array<T, _size>* _array) {
|
||||
for (size_t i = 0; i < std::min(_num_set, _size); ++i) {
|
||||
if constexpr (!std::is_array_v<T> &&
|
||||
std::is_destructible_v<std::remove_cv_t<T>>) {
|
||||
(*_array)[i].~T();
|
||||
} else if constexpr (std::is_array_v<T>) {
|
||||
auto ptr = (*_array)[i];
|
||||
call_destructor_on_array(sizeof(*ptr) / sizeof(**ptr), *ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef RFL_PARSING_CALL_DESTRUCTORS_ON_TUPLE_WHERE_NECESSARY_HPP_
|
||||
#define RFL_PARSING_CALL_DESTRUCTORS_ON_TUPLE_WHERE_NECESSARY_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Tuple.hpp"
|
||||
#include "call_destructors_where_necessary.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
/// Because of the way we have allocated the fields, we need to manually
|
||||
/// trigger the destructors.
|
||||
|
||||
template <class TupleType, unsigned long _size, int _i>
|
||||
void call_destructor_on_one_tuple_element_if_necessary(const size_t _num_set,
|
||||
TupleType* _tup) {
|
||||
using FieldType = tuple_element_t<_i, TupleType>;
|
||||
using ValueType = std::remove_cv_t<std::remove_pointer_t<FieldType>>;
|
||||
if constexpr (!std::is_array_v<ValueType> &&
|
||||
std::is_destructible_v<ValueType>) {
|
||||
if (_i < _num_set) {
|
||||
rfl::get<_i>(*_tup).~ValueType();
|
||||
}
|
||||
} else if constexpr (std::is_array_v<ValueType>) {
|
||||
if (_i < _num_set) {
|
||||
auto ptr = rfl::get<_i>(*_tup);
|
||||
call_destructor_on_array(sizeof(*ptr) / sizeof(**ptr), *ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class TupleType>
|
||||
void call_destructors_on_tuple_where_necessary(const size_t _num_set,
|
||||
TupleType* _tup) {
|
||||
[&]<int... is>(std::integer_sequence<int, is...>) {
|
||||
(call_destructor_on_one_tuple_element_if_necessary<
|
||||
TupleType, rfl::tuple_size_v<TupleType>, is>(_num_set, _tup),
|
||||
...);
|
||||
}
|
||||
(std::make_integer_sequence<int, rfl::tuple_size_v<TupleType>>());
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef RFL_PARSING_CALL_DESTRUCTORS_WHERE_NECESSARY_HPP_
|
||||
#define RFL_PARSING_CALL_DESTRUCTORS_WHERE_NECESSARY_HPP_
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../get.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
/// Because of the way we have allocated the fields, we need to manually
|
||||
/// trigger the destructors.
|
||||
|
||||
template <class T>
|
||||
void call_destructor_on_array(const size_t _size, T* _ptr) {
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
if constexpr (std::is_array_v<T>) {
|
||||
call_destructor_on_array(sizeof(*_ptr) / sizeof(**_ptr), *(_ptr + i));
|
||||
} else if constexpr (std::is_destructible_v<std::remove_cv_t<T>>) {
|
||||
(_ptr + i)->~T();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ViewType, unsigned long _size, int _i>
|
||||
void call_destructor_on_one_if_necessary(const std::array<bool, _size>& _set,
|
||||
ViewType* _view) {
|
||||
using FieldType = tuple_element_t<_i, typename ViewType::Fields>;
|
||||
using OriginalType = std::remove_cv_t<typename FieldType::Type>;
|
||||
using ValueType =
|
||||
std::remove_cvref_t<std::remove_pointer_t<typename FieldType::Type>>;
|
||||
if constexpr (!std::is_array_v<ValueType> &&
|
||||
std::is_pointer_v<OriginalType> &&
|
||||
std::is_destructible_v<ValueType>) {
|
||||
if (std::get<_i>(_set)) {
|
||||
rfl::get<_i>(*_view)->~ValueType();
|
||||
}
|
||||
} else if constexpr (std::is_array_v<ValueType>) {
|
||||
if (std::get<_i>(_set)) {
|
||||
auto ptr = rfl::get<_i>(*_view);
|
||||
call_destructor_on_array(sizeof(*ptr) / sizeof(**ptr), *ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ViewType, auto _size>
|
||||
void call_destructors_where_necessary(const std::array<bool, _size>& _set,
|
||||
ViewType* _view) {
|
||||
[&]<int... is>(std::integer_sequence<int, is...>) {
|
||||
(call_destructor_on_one_if_necessary<ViewType, _size, is>(_set, _view),
|
||||
...);
|
||||
}(std::make_integer_sequence<int, _size>());
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
42
build-config/reflect-cpp/include/rfl/parsing/is_empty.hpp
Normal file
42
build-config/reflect-cpp/include/rfl/parsing/is_empty.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef RFL_PARSING_IS_EMPTY_HPP_
|
||||
#define RFL_PARSING_IS_EMPTY_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../internal/has_reflection_method_v.hpp"
|
||||
#include "../internal/has_reflection_type_v.hpp"
|
||||
#include "../internal/is_rename.hpp"
|
||||
#include "../internal/is_skip.hpp"
|
||||
#include "is_map_like.hpp"
|
||||
#include "is_vector_like.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
static bool is_empty(const T& _var) {
|
||||
using Type = std::remove_cvref_t<T>;
|
||||
if constexpr (std::is_pointer_v<Type>) {
|
||||
return !_var || is_empty(*_var);
|
||||
} else if constexpr (internal::is_skip_v<Type>) {
|
||||
return Type::skip_serialization_;
|
||||
} else if constexpr (internal::has_reflection_type_v<Type>) {
|
||||
if constexpr (internal::has_reflection_method_v<Type>) {
|
||||
return is_empty(_var.reflection());
|
||||
} else {
|
||||
const auto& [r] = _var;
|
||||
return is_empty(r);
|
||||
}
|
||||
} else if constexpr (internal::is_rename_v<Type>) {
|
||||
return is_empty(_var.value());
|
||||
} else if constexpr (is_map_like_v<Type> || is_vector_like_v<Type>) {
|
||||
return _var.begin() == _var.end();
|
||||
} else {
|
||||
return !_var;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef RFL_PARSING_IS_FORWARD_LIST_HPP_
|
||||
#define RFL_PARSING_IS_FORWARD_LIST_HPP_
|
||||
|
||||
#include <forward_list>
|
||||
#include <type_traits>
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
class is_forward_list;
|
||||
|
||||
template <class T>
|
||||
class is_forward_list : public std::false_type {};
|
||||
|
||||
template <class T>
|
||||
class is_forward_list<std::forward_list<T>> : public std::true_type {};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
38
build-config/reflect-cpp/include/rfl/parsing/is_map_like.hpp
Normal file
38
build-config/reflect-cpp/include/rfl/parsing/is_map_like.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef RFL_PARSING_IS_MAP_LIKE_HPP_
|
||||
#define RFL_PARSING_IS_MAP_LIKE_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
class is_map_like;
|
||||
|
||||
template <class T>
|
||||
class is_map_like : public std::false_type {};
|
||||
|
||||
template <class K, class V>
|
||||
class is_map_like<std::map<K, V>> : public std::true_type {};
|
||||
|
||||
template <class K, class V>
|
||||
class is_map_like<std::multimap<K, V>> : public std::true_type {};
|
||||
|
||||
template <class K, class V, class Hash, class KeyEqual, class Allocator>
|
||||
class is_map_like<std::unordered_map<K, V, Hash, KeyEqual, Allocator>>
|
||||
: public std::true_type {};
|
||||
|
||||
template <class K, class V, class Hash, class KeyEqual, class Allocator>
|
||||
class is_map_like<std::unordered_multimap<K, V, Hash, KeyEqual, Allocator>>
|
||||
: public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_map_like_v =
|
||||
is_map_like<std::remove_cvref_t<std::remove_pointer_t<T>>>::value;
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef RFL_PARSING_IS_MAP_LIKE_NOT_MULTIMAP_HPP_
|
||||
#define RFL_PARSING_IS_MAP_LIKE_NOT_MULTIMAP_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
class is_map_like_not_multimap;
|
||||
|
||||
template <class T>
|
||||
class is_map_like_not_multimap : public std::false_type {};
|
||||
|
||||
template <class K, class V>
|
||||
class is_map_like_not_multimap<std::map<K, V>> : public std::true_type {};
|
||||
|
||||
template <class K, class V, class Hash, class KeyEqual, class Allocator>
|
||||
class is_map_like_not_multimap<
|
||||
std::unordered_map<K, V, Hash, KeyEqual, Allocator>>
|
||||
: public std::true_type {};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
58
build-config/reflect-cpp/include/rfl/parsing/is_required.hpp
Normal file
58
build-config/reflect-cpp/include/rfl/parsing/is_required.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef RFL_PARSING_IS_REQUIRED_HPP_
|
||||
#define RFL_PARSING_IS_REQUIRED_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../Generic.hpp"
|
||||
#include "../Rename.hpp"
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
#include "../internal/has_reflection_type_v.hpp"
|
||||
#include "../internal/is_rename.hpp"
|
||||
#include "is_map_like.hpp"
|
||||
#include "is_vector_like.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
/// Determines whether a field in a named tuple is required.
|
||||
/// General case - most fields are required.
|
||||
template <class T>
|
||||
class is_never_required;
|
||||
|
||||
template <class T>
|
||||
class is_never_required : public std::false_type {};
|
||||
|
||||
template <class T>
|
||||
class is_never_required<std::optional<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_never_required<std::shared_ptr<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_never_required<std::unique_ptr<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_never_required_v = is_never_required<T>::value;
|
||||
|
||||
template <class T, bool _ignore_empty_containers>
|
||||
consteval bool is_required() {
|
||||
using Type = std::remove_cvref_t<std::remove_pointer_t<T>>;
|
||||
if constexpr (internal::has_reflection_type_v<Type> &&
|
||||
!std::is_same_v<Type, rfl::Generic>) {
|
||||
return is_required<typename Type::ReflectionType,
|
||||
_ignore_empty_containers>();
|
||||
} else if constexpr (internal::is_rename_v<Type>) {
|
||||
return is_required<typename Type::Type, _ignore_empty_containers>();
|
||||
} else {
|
||||
return !(is_never_required_v<Type> ||
|
||||
(_ignore_empty_containers &&
|
||||
(is_vector_like_v<Type> || is_map_like_v<Type>)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
37
build-config/reflect-cpp/include/rfl/parsing/is_set_like.hpp
Normal file
37
build-config/reflect-cpp/include/rfl/parsing/is_set_like.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef RFL_PARSING_IS_SET_LIKE_HPP_
|
||||
#define RFL_PARSING_IS_SET_LIKE_HPP_
|
||||
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
class is_set_like;
|
||||
|
||||
template <class T>
|
||||
class is_set_like : public std::false_type {};
|
||||
|
||||
template <class T>
|
||||
class is_set_like<std::set<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_set_like<std::unordered_set<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_set_like<std::multiset<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_set_like<std::unordered_multiset<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_set_like_v =
|
||||
is_set_like<std::remove_cvref_t<std::remove_pointer_t<T>>>::value;
|
||||
|
||||
} // namespace parsing
|
||||
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef RFL_PARSING_ISTAGGEDUNIONWRAPPER_HPP_
|
||||
#define RFL_PARSING_ISTAGGEDUNIONWRAPPER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
#include "TaggedUnionWrapper.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
class is_tagged_union_wrapper;
|
||||
|
||||
template <class T>
|
||||
class is_tagged_union_wrapper : public std::false_type {};
|
||||
|
||||
template <class T, class TagType, internal::StringLiteral _discriminator,
|
||||
bool _as_const_pointer>
|
||||
class is_tagged_union_wrapper<
|
||||
TaggedUnionWrapperNoFields<T, TagType, _discriminator, _as_const_pointer>>
|
||||
: public std::true_type {};
|
||||
|
||||
template <class T, class TagType, internal::StringLiteral _discriminator,
|
||||
bool _as_const_pointer>
|
||||
class is_tagged_union_wrapper<
|
||||
TaggedUnionWrapperWithFields<T, TagType, _discriminator, _as_const_pointer>>
|
||||
: public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_tagged_union_wrapper_v =
|
||||
is_tagged_union_wrapper<std::remove_cvref_t<T>>::value;
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef RFL_PARSING_IS_VECTOR_LIKE_HPP_
|
||||
#define RFL_PARSING_IS_VECTOR_LIKE_HPP_
|
||||
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
class is_vector_like;
|
||||
|
||||
template <class T>
|
||||
class is_vector_like : public std::false_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::deque<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::forward_list<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::list<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::multiset<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::set<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::unordered_multiset<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::unordered_set<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
class is_vector_like<std::vector<T>> : public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_vector_like_v =
|
||||
is_vector_like<std::remove_cvref_t<std::remove_pointer_t<T>>>::value;
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef RFL_PARSING_ISVIEWREADER_HPP_
|
||||
#define RFL_PARSING_ISVIEWREADER_HPP_
|
||||
|
||||
#include "ViewReader.hpp"
|
||||
|
||||
namespace rfl ::parsing {
|
||||
|
||||
template <class T>
|
||||
class is_view_reader;
|
||||
|
||||
template <class T>
|
||||
class is_view_reader : public std::false_type {};
|
||||
|
||||
template <class R, class W, class NamedTupleType, class ProcessorsType>
|
||||
class is_view_reader<ViewReader<R, W, NamedTupleType, ProcessorsType>>
|
||||
: public std::true_type {};
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_view_reader_v =
|
||||
is_view_reader<std::remove_cvref_t<std::remove_pointer_t<T>>>::value;
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef RFL_PARSING_MAKETYPENAME_HPP_
|
||||
#define RFL_PARSING_MAKETYPENAME_HPP_
|
||||
|
||||
#include "../type_name_t.hpp"
|
||||
#include "is_tagged_union_wrapper.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
inline std::string replace_non_alphanumeric(std::string _str) {
|
||||
for (auto& ch : _str) {
|
||||
ch = std::isalnum(ch) ? ch : '_';
|
||||
}
|
||||
return _str;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
static std::string make_type_name() {
|
||||
if constexpr (is_tagged_union_wrapper_v<U>) {
|
||||
return replace_non_alphanumeric(type_name_t<typename U::Type>().str() +
|
||||
"__tagged");
|
||||
} else {
|
||||
return replace_non_alphanumeric(type_name_t<U>().str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef RFL_PARSING_SCHEMA_DEFINITION_HPP_
|
||||
#define RFL_PARSING_SCHEMA_DEFINITION_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "Type.hpp"
|
||||
|
||||
namespace rfl::parsing::schema {
|
||||
|
||||
struct Definition {
|
||||
/// Contains the root element of the schema definition.
|
||||
Type root_;
|
||||
|
||||
/// Contains the definitions to be referenced by Type::Reference.
|
||||
std::map<std::string, Type> definitions_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schema
|
||||
|
||||
#endif
|
||||
111
build-config/reflect-cpp/include/rfl/parsing/schema/Type.hpp
Normal file
111
build-config/reflect-cpp/include/rfl/parsing/schema/Type.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef RFL_PARSING_SCHEMA_TYPE_HPP_
|
||||
#define RFL_PARSING_SCHEMA_TYPE_HPP_
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../../Object.hpp"
|
||||
#include "../../Ref.hpp"
|
||||
#include "../../Variant.hpp"
|
||||
#include "ValidationType.hpp"
|
||||
#include "../../common.hpp"
|
||||
|
||||
namespace rfl::parsing::schema {
|
||||
|
||||
struct RFL_API Type {
|
||||
struct Boolean {};
|
||||
|
||||
struct Bytestring {};
|
||||
|
||||
struct Vectorstring {};
|
||||
|
||||
struct Int32 {};
|
||||
|
||||
struct Int64 {};
|
||||
|
||||
struct UInt32 {};
|
||||
|
||||
struct UInt64 {};
|
||||
|
||||
struct Integer {};
|
||||
|
||||
struct Float {};
|
||||
|
||||
struct Double {};
|
||||
|
||||
struct String {};
|
||||
|
||||
struct AnyOf {
|
||||
std::vector<Type> types_;
|
||||
};
|
||||
|
||||
struct Description {
|
||||
std::string description_;
|
||||
Ref<Type> type_;
|
||||
};
|
||||
|
||||
struct FixedSizeTypedArray {
|
||||
size_t size_;
|
||||
Ref<Type> type_;
|
||||
};
|
||||
|
||||
struct Literal {
|
||||
std::vector<std::string> values_;
|
||||
};
|
||||
|
||||
struct Object {
|
||||
rfl::Object<Type> types_;
|
||||
std::shared_ptr<Type> additional_properties_;
|
||||
};
|
||||
|
||||
/// All values are assumed to be required unless explicitly stated otherwise
|
||||
/// using this wrapper.
|
||||
struct Optional {
|
||||
Ref<Type> type_;
|
||||
};
|
||||
|
||||
/// The is necessary to resolve circular definitions. Refers to something in
|
||||
/// Definitions.
|
||||
struct Reference {
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
// A map with key type string.
|
||||
struct StringMap {
|
||||
Ref<Type> value_type_;
|
||||
};
|
||||
|
||||
struct Tuple {
|
||||
std::vector<Type> types_;
|
||||
};
|
||||
|
||||
struct TypedArray {
|
||||
Ref<Type> type_;
|
||||
};
|
||||
|
||||
struct Validated {
|
||||
Ref<Type> type_;
|
||||
ValidationType validation_;
|
||||
};
|
||||
|
||||
using VariantType =
|
||||
rfl::Variant<Boolean, Bytestring, Vectorstring, Int32, Int64, UInt32, UInt64, Integer,
|
||||
Float, Double, String, AnyOf, Description,
|
||||
FixedSizeTypedArray, Literal, Object, Optional, Reference,
|
||||
StringMap, Tuple, TypedArray, Validated>;
|
||||
|
||||
Type();
|
||||
|
||||
Type(const VariantType& _variant);
|
||||
|
||||
~Type();
|
||||
|
||||
/// A type can be determined to be any of the above.
|
||||
VariantType variant_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schema
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef RFL_PARSING_SCHEMA_VALIDATIONTYPE_HPP_
|
||||
#define RFL_PARSING_SCHEMA_VALIDATIONTYPE_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../../Ref.hpp"
|
||||
#include "../../Variant.hpp"
|
||||
|
||||
namespace rfl::parsing::schema {
|
||||
|
||||
struct ValidationType {
|
||||
struct AllOf {
|
||||
std::vector<ValidationType> types_;
|
||||
};
|
||||
|
||||
struct AnyOf {
|
||||
std::vector<ValidationType> types_;
|
||||
};
|
||||
|
||||
struct EqualTo {
|
||||
rfl::Variant<double, int> value_;
|
||||
};
|
||||
|
||||
struct ExclusiveMaximum {
|
||||
rfl::Variant<double, int> value_;
|
||||
};
|
||||
|
||||
struct ExclusiveMinimum {
|
||||
rfl::Variant<double, int> value_;
|
||||
};
|
||||
|
||||
struct Maximum {
|
||||
rfl::Variant<double, int> value_;
|
||||
};
|
||||
|
||||
struct Minimum {
|
||||
rfl::Variant<double, int> value_;
|
||||
};
|
||||
|
||||
struct NotEqualTo {
|
||||
rfl::Variant<double, int> value_;
|
||||
};
|
||||
|
||||
struct OneOf {
|
||||
std::vector<ValidationType> types_;
|
||||
};
|
||||
|
||||
struct Regex {
|
||||
std::string pattern_;
|
||||
};
|
||||
|
||||
struct Size {
|
||||
Ref<ValidationType> size_limit_;
|
||||
};
|
||||
|
||||
using VariantType =
|
||||
rfl::Variant<AllOf, AnyOf, EqualTo, ExclusiveMaximum, ExclusiveMinimum,
|
||||
Maximum, Minimum, OneOf, NotEqualTo, Regex, Size>;
|
||||
|
||||
/// A type can be determined to be any of the above.
|
||||
VariantType variant_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schema
|
||||
|
||||
#endif
|
||||
21
build-config/reflect-cpp/include/rfl/parsing/schema/make.hpp
Normal file
21
build-config/reflect-cpp/include/rfl/parsing/schema/make.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef RFL_PARSING_SCHEMA_MAKE_HPP_
|
||||
#define RFL_PARSING_SCHEMA_MAKE_HPP_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../Parser.hpp"
|
||||
#include "Definition.hpp"
|
||||
#include "Type.hpp"
|
||||
|
||||
namespace rfl::parsing::schema {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
Definition make() {
|
||||
std::map<std::string, Type> definitions;
|
||||
auto root = Parser<R, W, T, ProcessorsType>::to_schema(&definitions);
|
||||
return Definition{.root_ = root, .definitions_ = definitions};
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::schema
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_ISSCHEMAFULREADER_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_ISSCHEMAFULREADER_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "../../Result.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
using MockVariantType = std::variant<std::string, int>;
|
||||
|
||||
template <class R>
|
||||
struct MockMapReader {
|
||||
void read(const std::string_view&, typename R::InputVarType&) const {}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
struct MockObjectReader {
|
||||
void read(const int, typename R::InputVarType&) const {}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
struct MockUnionReader {
|
||||
static rfl::Result<MockVariantType> read(const R&, const size_t,
|
||||
typename R::InputVarType&) {
|
||||
return error("This is a mock type.");
|
||||
}
|
||||
};
|
||||
|
||||
template <class R>
|
||||
concept IsSchemafulReader =
|
||||
requires(R r, typename R::InputVarType var, typename R::InputObjectType obj,
|
||||
typename R::InputMapType m, typename R::InputUnionType u,
|
||||
MockMapReader<R> map_reader, MockObjectReader<R> object_reader) {
|
||||
/// A schemaful reader needs to differentiate between objects, for which
|
||||
/// the field names are known at compile time and maps, for which the
|
||||
/// field names are not known at compile time.
|
||||
{ r.read_map(map_reader, m) } -> std::same_as<std::optional<Error>>;
|
||||
|
||||
/// A schemaful reader can read fields by order and does not have to
|
||||
/// compare strings - the correct order of the fields is guaranteed by the
|
||||
/// schema.
|
||||
{
|
||||
r.read_object(object_reader, obj)
|
||||
} -> std::same_as<std::optional<Error>>;
|
||||
|
||||
/// A schemaful reader needs an explicit union type.
|
||||
{
|
||||
r.template read_union<MockVariantType, MockUnionReader<R>>(u)
|
||||
} -> std::same_as<rfl::Result<MockVariantType>>;
|
||||
|
||||
/// It needs to be possible to transform variables to maps.
|
||||
{ r.to_map(var) } -> std::same_as<rfl::Result<typename R::InputMapType>>;
|
||||
|
||||
/// It needs to be possible to transform variables to unions.
|
||||
{
|
||||
r.to_union(var)
|
||||
} -> std::same_as<rfl::Result<typename R::InputUnionType>>;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_ISSCHEMAFULWRITER_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_ISSCHEMAFULWRITER_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <class W>
|
||||
concept IsSchemafulWriter = requires(
|
||||
W w, typename W::OutputVarType var, typename W::OutputArrayType arr,
|
||||
typename W::OutputMapType m, typename W::OutputObjectType obj,
|
||||
typename W::OutputUnionType u, size_t index, size_t size,
|
||||
std::string_view name, std::string val) {
|
||||
/// A schemaful writer needs to know two additional types:
|
||||
/// 1) Maps - unlike objects, their field names are not known at compile time.
|
||||
/// 2) Unions - schemaful formats need explicit union types.
|
||||
|
||||
/// Sets an empty map as the root element of the document.
|
||||
/// Some serialization formats require you to pass the expected size in
|
||||
/// advance. If you are not working with such a format, you can ignore the
|
||||
/// parameter `size`. Returns the new array for further modification.
|
||||
{ w.map_as_root(size) } -> std::same_as<typename W::OutputMapType>;
|
||||
|
||||
/// Sets an empty union as the root element of the document.
|
||||
{ w.union_as_root() } -> std::same_as<typename W::OutputUnionType>;
|
||||
|
||||
/// Adds an empty array to an existing map. Returns the new
|
||||
/// array for further modification.
|
||||
{
|
||||
w.add_array_to_map(name, size, &m)
|
||||
} -> std::same_as<typename W::OutputArrayType>;
|
||||
|
||||
/// Adds an empty array to an existing union.
|
||||
/// The index refers to the index of the element in the union.
|
||||
/// Returns the new array for further modification.
|
||||
{
|
||||
w.add_array_to_union(index, size, &u)
|
||||
} -> std::same_as<typename W::OutputArrayType>;
|
||||
|
||||
/// Adds an empty map to an existing array. Returns the new
|
||||
/// map for further modification.
|
||||
{ w.add_map_to_array(size, &arr) } -> std::same_as<typename W::OutputMapType>;
|
||||
|
||||
/// Adds an empty map to an existing map. The key or name of the field
|
||||
/// is signified by `name`. Returns the new map for further modification.
|
||||
{
|
||||
w.add_map_to_map(name, size, &m)
|
||||
} -> std::same_as<typename W::OutputMapType>;
|
||||
|
||||
/// Adds an empty map to an existing object. The key or name of the field
|
||||
/// is signified by `name`. Returns the new map for further modification.
|
||||
{
|
||||
w.add_map_to_object(name, size, &obj)
|
||||
} -> std::same_as<typename W::OutputMapType>;
|
||||
|
||||
/// Adds an empty map to an existing union.
|
||||
/// The index refers to the index of the element in the union.
|
||||
/// Returns the new map for further modification.
|
||||
{
|
||||
w.add_map_to_union(index, size, &u)
|
||||
} -> std::same_as<typename W::OutputMapType>;
|
||||
|
||||
/// Adds an empty object to an existing map. The key or name of the field
|
||||
/// is signified by `name`. Returns the new object for further modification.
|
||||
{
|
||||
w.add_object_to_map(name, size, &m)
|
||||
} -> std::same_as<typename W::OutputObjectType>;
|
||||
|
||||
/// Adds an empty object to an existing union.
|
||||
/// The index refers to the index of the element in the union.
|
||||
/// Returns the new object for further modification.
|
||||
{
|
||||
w.add_object_to_union(index, size, &u)
|
||||
} -> std::same_as<typename W::OutputObjectType>;
|
||||
|
||||
/// Adds an empty union to an existing array. Returns the new
|
||||
/// union for further modification.
|
||||
{ w.add_union_to_array(&arr) } -> std::same_as<typename W::OutputUnionType>;
|
||||
|
||||
/// Adds an empty union to an existing map. The key or name of the field
|
||||
/// is signified by `name`. Returns the new union for further modification.
|
||||
{ w.add_union_to_map(name, &m) } -> std::same_as<typename W::OutputUnionType>;
|
||||
|
||||
/// Adds an empty union to an existing object. The key or name of the field
|
||||
/// is signified by `name`. Returns the new union for further modification.
|
||||
{
|
||||
w.add_union_to_object(name, &obj)
|
||||
} -> std::same_as<typename W::OutputUnionType>;
|
||||
|
||||
/// Adds an empty union to an existing union.
|
||||
/// The index refers to the index of the element in the union.
|
||||
/// Returns the new union for further modification.
|
||||
{
|
||||
w.add_union_to_union(index, &u)
|
||||
} -> std::same_as<typename W::OutputUnionType>;
|
||||
|
||||
/// Adds a null value to a map. Returns an
|
||||
/// OutputVarType containing the null value.
|
||||
{ w.add_null_to_map(name, &m) } -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds a basic value (bool, numeric, string) to an existing map. The key
|
||||
/// or name of the field is signified by `name`. Returns an
|
||||
/// OutputVarType containing the new value.
|
||||
{
|
||||
w.add_value_to_map(name, val, &m)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds a null value to a union. Returns an
|
||||
/// OutputVarType containing the null value.
|
||||
{ w.add_null_to_union(index, &u) } -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Adds a basic value (bool, numeric, string) to an existing union. The key
|
||||
/// or name of the field is signified by `name`. Returns an
|
||||
/// OutputVarType containing the new value.
|
||||
{
|
||||
w.add_value_to_union(index, val, &u)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
/// Signifies to the writer that we do not want to add any further elements to
|
||||
/// this map. Some serialization formats require this. If you are working
|
||||
/// with a serialization format that doesn't, just leave the function empty.
|
||||
{ w.end_map(&m) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_OPTIONALREADER_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_OPTIONALREADER_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../Result.hpp"
|
||||
#include "../Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
struct OptionalReader {
|
||||
static Result<std::optional<T>> read(
|
||||
const R& _r, const size_t _index,
|
||||
const typename R::InputVarType& _var) noexcept {
|
||||
if (_index == 1) {
|
||||
return std::optional<T>();
|
||||
}
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_SHAREDPTRREADER_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_SHAREDPTRREADER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../Result.hpp"
|
||||
#include "../Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
struct SharedPtrReader {
|
||||
static Result<std::shared_ptr<T>> read(
|
||||
const R& _r, const size_t _index,
|
||||
const typename R::InputVarType& _var) noexcept {
|
||||
if (_index == 1) {
|
||||
return std::shared_ptr<T>();
|
||||
}
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform([](T&& _t) { return std::make_shared<T>(std::move(_t)); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_UNIQUEPTRREADER_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_UNIQUEPTRREADER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../Result.hpp"
|
||||
#include "../Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <class R, class W, class T, class ProcessorsType>
|
||||
struct UniquePtrReader {
|
||||
static Result<std::unique_ptr<T>> read(
|
||||
const R& _r, const size_t _index,
|
||||
const typename R::InputVarType& _var) noexcept {
|
||||
if (_index == 1) {
|
||||
return std::unique_ptr<T>();
|
||||
}
|
||||
return Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform([](T&& _t) { return std::make_unique<T>(std::move(_t)); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_VARIANTREADER_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_VARIANTREADER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../../Result.hpp"
|
||||
#include "../../internal/nth_element_t.hpp"
|
||||
#include "../Parser_base.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <class R, class W, class VariantType, class ProcessorsType,
|
||||
class... AlternativeTypes>
|
||||
class VariantReader {
|
||||
public:
|
||||
static Result<VariantType> read(
|
||||
const R& _r, const size_t _index,
|
||||
const typename R::InputVarType& _var) noexcept {
|
||||
return [&]<size_t... _is>(std::integer_sequence<size_t, _is...>) {
|
||||
Result<VariantType> result =
|
||||
error("Could not parse union: Index out of bounds.");
|
||||
(try_one_type<_is>(_r, _index, _var, &result), ...);
|
||||
return result;
|
||||
}(std::make_integer_sequence<size_t, sizeof...(AlternativeTypes)>());
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t _i>
|
||||
static void try_one_type(const R& _r, const size_t _index,
|
||||
const typename R::InputVarType& _var,
|
||||
Result<VariantType>* _result) noexcept {
|
||||
if (_index == _i) {
|
||||
using T = internal::nth_element_t<_i, AlternativeTypes...>;
|
||||
*_result =
|
||||
Parser<R, W, std::remove_cvref_t<T>, ProcessorsType>::read(_r, _var)
|
||||
.transform(
|
||||
[](auto&& _val) -> VariantType { return std::move(_val); });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_TUPLETONAMEDTUPLE_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_TUPLETONAMEDTUPLE_HPP_
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../../Field.hpp"
|
||||
#include "../../Tuple.hpp"
|
||||
#include "../../internal/StringLiteral.hpp"
|
||||
#include "../../make_named_tuple.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <int _i>
|
||||
inline consteval auto to_field_name() {
|
||||
return internal::StringLiteral<5>('f',
|
||||
static_cast<char>('0' + ((_i / 100) % 10)),
|
||||
static_cast<char>('0' + ((_i / 10) % 10)),
|
||||
static_cast<char>('0' + (_i % 10)));
|
||||
}
|
||||
|
||||
template <int _i>
|
||||
inline auto to_field(const auto& _t) {
|
||||
using T = std::remove_cvref_t<decltype(_t)>;
|
||||
return rfl::Field<to_field_name<_i>(), const T*>(&_t);
|
||||
}
|
||||
|
||||
/// Schemaful formats often don't have an explicit tuple representation.
|
||||
/// This is the required workaround.
|
||||
template <class... Ts>
|
||||
auto tuple_to_named_tuple(const Tuple<Ts...>& _tup) {
|
||||
static_assert(sizeof...(Ts) <= 1000,
|
||||
"The tuple cannot contain more than 1000 elements.");
|
||||
return [&]<int... _is>(std::integer_sequence<int, _is...>) {
|
||||
return rfl::make_named_tuple(to_field<_is>(rfl::get<_is>(_tup))...);
|
||||
}(std::make_integer_sequence<int, sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
/// Schemaful formats often don't have an explicit tuple representation.
|
||||
/// This is the required workaround.
|
||||
template <class... Ts>
|
||||
auto tuple_to_named_tuple(const std::tuple<Ts...>& _tup) {
|
||||
static_assert(sizeof...(Ts) <= 1000,
|
||||
"The tuple cannot contain more than 1000 elements.");
|
||||
return [&]<int... _is>(std::integer_sequence<int, _is...>) {
|
||||
return rfl::make_named_tuple(to_field<_is>(std::get<_is>(_tup))...);
|
||||
}(std::make_integer_sequence<int, sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_TUPLETONAMEDTUPLE_T_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_TUPLETONAMEDTUPLE_T_HPP_
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "../../NamedTuple.hpp"
|
||||
#include "../../Tuple.hpp"
|
||||
#include "tuple_to_named_tuple.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
template <class IntegerSequence, class... Ts>
|
||||
struct ToNamedTuple;
|
||||
|
||||
template <int... _is, class... Ts>
|
||||
struct ToNamedTuple<std::integer_sequence<int, _is...>, Ts...> {
|
||||
using Type = NamedTuple<Field<to_field_name<_is>(), Ts>...>;
|
||||
};
|
||||
|
||||
template <class TupleType>
|
||||
struct TupleToNamedTuple;
|
||||
|
||||
template <class... Ts>
|
||||
struct TupleToNamedTuple<Tuple<Ts...>> {
|
||||
using Type =
|
||||
typename ToNamedTuple<std::make_integer_sequence<int, sizeof...(Ts)>,
|
||||
Ts...>::Type;
|
||||
};
|
||||
|
||||
template <class... Ts>
|
||||
struct TupleToNamedTuple<std::tuple<Ts...>> {
|
||||
using Type =
|
||||
typename ToNamedTuple<std::make_integer_sequence<int, sizeof...(Ts)>,
|
||||
Ts...>::Type;
|
||||
};
|
||||
|
||||
/// Given
|
||||
/// std::tuple<T1, T2, T3, ...> or rfl::Tuple<T1, T2, T3, ...>
|
||||
/// return
|
||||
/// NamedTuple<Field<"f000", T1>, Field<"f001", T2>, Field<"f002", T3>, ...>
|
||||
template <class T>
|
||||
using tuple_to_named_tuple_t = typename TupleToNamedTuple<T>::Type;
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef RFL_PARSING_SCHEMAFUL_TUPLETOOBJECT_HPP_
|
||||
#define RFL_PARSING_SCHEMAFUL_TUPLETOOBJECT_HPP_
|
||||
|
||||
#include "../schema/Type.hpp"
|
||||
#include "../../common.hpp"
|
||||
|
||||
namespace rfl::parsing::schemaful {
|
||||
|
||||
/// Schemaful formats often don't have an explicit tuple representation.
|
||||
/// This is the required workaround.
|
||||
RFL_API schema::Type::Object tuple_to_object(const schema::Type::Tuple& _tup);
|
||||
|
||||
} // namespace rfl::parsing::schemaful
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef RFL_PARSING_SUPPORTSATTRIBUTES_HPP_
|
||||
#define RFL_PARSING_SUPPORTSATTRIBUTES_HPP_
|
||||
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
|
||||
#include "../Result.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
/// Determines whether a writer supports attributes.
|
||||
template <class W>
|
||||
concept supports_attributes = requires(W w, std::string_view name,
|
||||
typename W::OutputObjectType obj,
|
||||
bool is_attribute) {
|
||||
{
|
||||
w.add_value_to_object(name, name, &obj, is_attribute)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
|
||||
{
|
||||
w.add_null_to_object(name, &obj, is_attribute)
|
||||
} -> std::same_as<typename W::OutputVarType>;
|
||||
};
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef RFL_PARSING_TABULAR_ARROWREADER_HPP_
|
||||
#define RFL_PARSING_TABULAR_ARROWREADER_HPP_
|
||||
|
||||
#include <arrow/api.h>
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../../Processors.hpp"
|
||||
#include "../../Result.hpp"
|
||||
#include "../../Tuple.hpp"
|
||||
#include "../../apply.hpp"
|
||||
#include "../../get.hpp"
|
||||
#include "../../named_tuple_t.hpp"
|
||||
#include "../../to_view.hpp"
|
||||
#include "../../view_t.hpp"
|
||||
#include "../call_destructors_where_necessary.hpp"
|
||||
#include "make_chunked_array_iterators.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class VecType, SerializationType _s, class... Ps>
|
||||
class ArrowReader {
|
||||
static_assert(!Processors<Ps...>::add_tags_to_variants_,
|
||||
"rfl::AddTagsToVariants cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::add_namespaced_tags_to_variants_,
|
||||
"rfl::AddNamespacedTagsToVariants cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::all_required_,
|
||||
"rfl::NoOptionals cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::default_if_missing_,
|
||||
"rfl::DefaultIfMissing cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::no_extra_fields_,
|
||||
"rfl::NoExtraFields cannot be used for tabular data (neither "
|
||||
"can rfl::ExtraFields).");
|
||||
static_assert(!Processors<Ps...>::no_field_names_,
|
||||
"rfl::NoFieldNames cannot be used for tabular data.");
|
||||
|
||||
public:
|
||||
using ValueType = typename std::remove_cvref_t<typename VecType::value_type>;
|
||||
|
||||
static Result<ArrowReader> make(const std::shared_ptr<arrow::Table>& _table) {
|
||||
try {
|
||||
return ArrowReader(_table);
|
||||
} catch (const std::exception& e) {
|
||||
return error(std::string("Could not create ArrowReader: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
~ArrowReader() = default;
|
||||
|
||||
Result<VecType> read() const noexcept {
|
||||
return make_chunked_array_iterators<named_tuple_t<ValueType, Ps...>, _s>(
|
||||
table_)
|
||||
.and_then([&](auto chunked_array_iterators) -> Result<VecType> {
|
||||
VecType result;
|
||||
while (!end(chunked_array_iterators)) {
|
||||
auto value = new_value(&chunked_array_iterators);
|
||||
if (!value) {
|
||||
return error(value.error().what());
|
||||
}
|
||||
result.emplace_back(std::move(*value));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ArrowReader(const std::shared_ptr<arrow::Table>& _table)
|
||||
: table_(Ref<arrow::Table>::make(_table).value()) {}
|
||||
|
||||
bool end(const auto& _chunked_array_iterators) const {
|
||||
return apply(
|
||||
[](const auto&... _its) { return (false || ... || _its.end()); },
|
||||
_chunked_array_iterators);
|
||||
}
|
||||
|
||||
Result<ValueType> new_value(auto* _chunked_array_iterators) const noexcept {
|
||||
alignas(ValueType) unsigned char buf[sizeof(ValueType)]{};
|
||||
auto ptr = internal::ptr_cast<ValueType*>(&buf);
|
||||
|
||||
auto view = to_view(*ptr);
|
||||
|
||||
using ViewType = std::remove_cvref_t<decltype(view)>;
|
||||
|
||||
try {
|
||||
const auto set_one = [&]<size_t _i>(std::integral_constant<size_t, _i>) {
|
||||
using FieldType = tuple_element_t<_i, typename ViewType::Fields>;
|
||||
using T = std::remove_cvref_t<
|
||||
std::remove_pointer_t<typename FieldType::Type>>;
|
||||
auto res = *_chunked_array_iterators->template get<_i>();
|
||||
if (!res) {
|
||||
destroy_value<_i>(&view);
|
||||
throw std::runtime_error(
|
||||
std::string("Field '") + typename FieldType::Name().str() +
|
||||
std::string("' could not be set: ") + res.error().what());
|
||||
}
|
||||
::new (view.template get<_i>()) T(std::move(*res));
|
||||
++_chunked_array_iterators->template get<_i>();
|
||||
};
|
||||
|
||||
[&]<size_t... _is>(std::integer_sequence<size_t, _is...>) {
|
||||
(set_one(std::integral_constant<size_t, _is>{}), ...);
|
||||
}(std::make_integer_sequence<size_t, view.size()>());
|
||||
} catch (const std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
|
||||
return std::move(*ptr);
|
||||
}
|
||||
|
||||
template <size_t _i, class ViewType>
|
||||
void destroy_value(ViewType* _view) const {
|
||||
static_assert(_i < ViewType::size(), "_i out of bounds.");
|
||||
auto set = std::array<bool, ViewType::size()>();
|
||||
for (size_t i = 0; i < _i; ++i) {
|
||||
set[i] = true;
|
||||
}
|
||||
for (size_t i = _i; i < ViewType::size(); ++i) {
|
||||
set[i] = false;
|
||||
}
|
||||
call_destructors_where_necessary(set, _view);
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<arrow::Table> table_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,956 @@
|
||||
#ifndef RFL_PARSING_TABULAR_ARROWTYPES_HPP_
|
||||
#define RFL_PARSING_TABULAR_ARROWTYPES_HPP_
|
||||
|
||||
#include <arrow/api.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "../../Box.hpp"
|
||||
#include "../../NamedTuple.hpp"
|
||||
#include "../../Ref.hpp"
|
||||
#include "../../Rename.hpp"
|
||||
#include "../../Timestamp.hpp"
|
||||
#include "../../Tuple.hpp"
|
||||
#include "../../concepts.hpp"
|
||||
#include "../../enums.hpp"
|
||||
#include "../../internal/StringLiteral.hpp"
|
||||
#include "../../internal/has_reflection_type_v.hpp"
|
||||
#include "../../internal/ptr_cast.hpp"
|
||||
#include "../../named_tuple_t.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
enum class SerializationType { csv, parquet };
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowTypes;
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
Result<Ref<typename ArrowTypes<T, _s>::ArrayType>> transform_numerical_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) noexcept;
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<bool, _s> {
|
||||
using ArrayType = arrow::BooleanArray;
|
||||
using BuilderType = arrow::BooleanBuilder;
|
||||
|
||||
static auto data_type() { return arrow::boolean(); }
|
||||
|
||||
static void add_to_builder(const bool _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
if (_arr->type()->Equals(data_type())) {
|
||||
return Ref<ArrayType>::make(std::static_pointer_cast<ArrayType>(_arr));
|
||||
} else {
|
||||
return error("Expected boolean array, got " + _arr->type()->ToString() +
|
||||
".");
|
||||
}
|
||||
}
|
||||
|
||||
static Result<bool> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<uint8_t, _s> {
|
||||
using ArrayType = arrow::UInt8Array;
|
||||
using BuilderType = arrow::UInt8Builder;
|
||||
using T = uint8_t;
|
||||
|
||||
static auto data_type() { return arrow::uint8(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<uint8_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<uint16_t, _s> {
|
||||
using ArrayType = arrow::UInt16Array;
|
||||
using BuilderType = arrow::UInt16Builder;
|
||||
using T = uint16_t;
|
||||
|
||||
static auto data_type() { return arrow::uint16(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<uint16_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<uint32_t, _s> {
|
||||
using ArrayType = arrow::UInt32Array;
|
||||
using BuilderType = arrow::UInt32Builder;
|
||||
using T = uint32_t;
|
||||
|
||||
static auto data_type() { return arrow::uint32(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<uint32_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<uint64_t, _s> {
|
||||
using ArrayType = arrow::UInt64Array;
|
||||
using BuilderType = arrow::UInt64Builder;
|
||||
using T = uint64_t;
|
||||
|
||||
static auto data_type() { return arrow::uint64(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<uint64_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<int8_t, _s> {
|
||||
using ArrayType = arrow::Int8Array;
|
||||
using BuilderType = arrow::Int8Builder;
|
||||
using T = int8_t;
|
||||
|
||||
static auto data_type() { return arrow::int8(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<int8_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<int16_t, _s> {
|
||||
using ArrayType = arrow::Int16Array;
|
||||
using BuilderType = arrow::Int16Builder;
|
||||
using T = int16_t;
|
||||
|
||||
static auto data_type() { return arrow::int16(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<int16_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<int32_t, _s> {
|
||||
using ArrayType = arrow::Int32Array;
|
||||
using BuilderType = arrow::Int32Builder;
|
||||
using T = int32_t;
|
||||
|
||||
static auto data_type() { return arrow::int32(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<int32_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<int64_t, _s> {
|
||||
using ArrayType = arrow::Int64Array;
|
||||
using BuilderType = arrow::Int64Builder;
|
||||
using T = int64_t;
|
||||
|
||||
static auto data_type() { return arrow::int64(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<int64_t> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<float, _s> {
|
||||
using ArrayType = arrow::FloatArray;
|
||||
using BuilderType = arrow::FloatBuilder;
|
||||
using T = float;
|
||||
|
||||
static auto data_type() { return arrow::float32(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<float> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<double, _s> {
|
||||
using ArrayType = arrow::DoubleArray;
|
||||
using BuilderType = arrow::DoubleBuilder;
|
||||
using T = double;
|
||||
|
||||
static auto data_type() { return arrow::float64(); }
|
||||
|
||||
static void add_to_builder(const auto _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return transform_numerical_array<T, _s>(_arr);
|
||||
}
|
||||
|
||||
static Result<double> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return _chunk->Value(_ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <SerializationType _s>
|
||||
struct ArrowTypes<std::string, _s> {
|
||||
using ArrayType = arrow::StringArray;
|
||||
using BuilderType = arrow::StringBuilder;
|
||||
|
||||
static auto data_type() { return arrow::utf8(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
if (_arr->type()->Equals(data_type())) {
|
||||
return Ref<ArrayType>::make(std::static_pointer_cast<ArrayType>(_arr));
|
||||
} else {
|
||||
return error("Expected string array, got " + _arr->type()->ToString() +
|
||||
".");
|
||||
}
|
||||
}
|
||||
|
||||
static Result<std::string> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return std::string(_chunk->Value(_ix));
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
requires enchantum::Enum<T>
|
||||
struct ArrowTypes<T, _s> {
|
||||
using ArrayType = arrow::StringArray;
|
||||
using BuilderType = arrow::StringBuilder;
|
||||
|
||||
static auto data_type() { return arrow::utf8(); }
|
||||
|
||||
static void add_to_builder(const T _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(enum_to_string(_val));
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<std::string, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static Result<T> get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return string_to_enum<T>(std::string(_chunk->Value(_ix)));
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
requires concepts::ContiguousByteContainer<T>
|
||||
struct ArrowTypes<T, _s> {
|
||||
using ArrayType = arrow::BinaryArray;
|
||||
using BuilderType = arrow::BinaryBuilder;
|
||||
|
||||
static auto data_type() { return arrow::binary(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(
|
||||
internal::ptr_cast<const uint8_t*>(_val.data()), _val.size());
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
if (_arr->type()->Equals(data_type())) {
|
||||
return Ref<ArrayType>::make(std::static_pointer_cast<ArrayType>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(arrow::utf8())) {
|
||||
return transform_string(
|
||||
std::static_pointer_cast<arrow::StringArray>(_arr));
|
||||
|
||||
} else {
|
||||
return error("Expected binary or string array, got " +
|
||||
_arr->type()->ToString() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
static Result<T> get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
const auto begin = internal::ptr_cast<const typename T::value_type*>(
|
||||
_chunk->Value(_ix).data());
|
||||
return T(begin, begin + _chunk->Value(_ix).size());
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
|
||||
static Result<Ref<arrow::BinaryArray>> transform_string(
|
||||
const std::shared_ptr<arrow::StringArray>& _arr) noexcept {
|
||||
if (!_arr) {
|
||||
return error(
|
||||
"transform_string: std::shared_ptr not set. This is a "
|
||||
"bug, please report.");
|
||||
}
|
||||
|
||||
auto builder = arrow::BinaryBuilder();
|
||||
|
||||
for (int64_t i = 0; i < _arr->length(); ++i) {
|
||||
if (_arr->IsNull(i)) {
|
||||
const auto status = builder.AppendNull();
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else {
|
||||
const std::string_view s = _arr->Value(i);
|
||||
const auto status = builder.Append(
|
||||
internal::ptr_cast<const uint8_t*>(s.data()), s.size());
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<arrow::Array> res;
|
||||
const auto status = builder.Finish(&res);
|
||||
return Ref<arrow::BinaryArray>::make(
|
||||
std::static_pointer_cast<arrow::BinaryArray>(res));
|
||||
}
|
||||
};
|
||||
|
||||
template <internal::StringLiteral _format, SerializationType _s>
|
||||
struct ArrowTypes<Timestamp<_format>, _s> {
|
||||
enum class TimeUnit { day, second, milli, micro, nano, string };
|
||||
|
||||
using ArrayType = arrow::TimestampArray;
|
||||
using BuilderType = arrow::TimestampBuilder;
|
||||
|
||||
static auto data_type() { return arrow::timestamp(arrow::TimeUnit::MILLI); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val.to_time_t() * 1000);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
if (_arr->type()->Equals(data_type())) {
|
||||
return Ref<ArrayType>::make(std::static_pointer_cast<ArrayType>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(
|
||||
arrow::timestamp(arrow::TimeUnit::SECOND))) {
|
||||
return transform_time_stamp<TimeUnit::second>(
|
||||
std::static_pointer_cast<arrow::TimestampArray>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(arrow::timestamp(arrow::TimeUnit::MICRO))) {
|
||||
return transform_time_stamp<TimeUnit::micro>(
|
||||
std::static_pointer_cast<arrow::TimestampArray>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(arrow::timestamp(arrow::TimeUnit::NANO))) {
|
||||
return transform_time_stamp<TimeUnit::nano>(
|
||||
std::static_pointer_cast<arrow::TimestampArray>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(arrow::date32())) {
|
||||
return transform_time_stamp<TimeUnit::day>(
|
||||
std::static_pointer_cast<arrow::Date32Array>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(arrow::date64())) {
|
||||
return transform_time_stamp<TimeUnit::milli>(
|
||||
std::static_pointer_cast<arrow::Date64Array>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(arrow::utf8())) {
|
||||
return transform_time_stamp<TimeUnit::string>(
|
||||
std::static_pointer_cast<arrow::StringArray>(_arr));
|
||||
|
||||
} else {
|
||||
return error("Expected timestamp, date32, date64 or string array, got " +
|
||||
_arr->type()->ToString() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Timestamp<_format>> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return Timestamp<_format>(_chunk->Value(_ix) / 1000);
|
||||
}
|
||||
|
||||
static auto make_builder() {
|
||||
return BuilderType(data_type(), arrow::default_memory_pool());
|
||||
}
|
||||
|
||||
template <TimeUnit _unit, class SourceArrayType>
|
||||
static Result<Ref<arrow::TimestampArray>> transform_time_stamp(
|
||||
const std::shared_ptr<SourceArrayType>& _arr) noexcept {
|
||||
if (!_arr) {
|
||||
return error(
|
||||
"transform_time_stamp: std::shared_ptr not set. This is a "
|
||||
"bug, please report.");
|
||||
}
|
||||
|
||||
auto builder =
|
||||
arrow::TimestampBuilder(data_type(), arrow::default_memory_pool());
|
||||
|
||||
for (int64_t i = 0; i < _arr->length(); ++i) {
|
||||
if (_arr->IsNull(i)) {
|
||||
const auto status = builder.AppendNull();
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else {
|
||||
if constexpr (_unit == TimeUnit::day) {
|
||||
const auto status = builder.Append(
|
||||
static_cast<int64_t>(_arr->Value(i)) * 1000 * 24 * 60 * 60);
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else if constexpr (_unit == TimeUnit::second) {
|
||||
const auto status =
|
||||
builder.Append(static_cast<int64_t>(_arr->Value(i) * 1000));
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else if constexpr (_unit == TimeUnit::milli) {
|
||||
const auto status =
|
||||
builder.Append(static_cast<int64_t>(_arr->Value(i)));
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else if constexpr (_unit == TimeUnit::micro) {
|
||||
const auto status =
|
||||
builder.Append(static_cast<int64_t>(_arr->Value(i) / 1000));
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else if constexpr (_unit == TimeUnit::nano) {
|
||||
const auto status =
|
||||
builder.Append(static_cast<int64_t>(_arr->Value(i) / 1000000));
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else if constexpr (_unit == TimeUnit::string) {
|
||||
const auto ts = Timestamp<_format>::make(std::string(_arr->Value(i)));
|
||||
if (!ts) {
|
||||
return error(ts.error().what());
|
||||
}
|
||||
const auto status = builder.Append(ts->to_time_t() * 1000);
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else {
|
||||
static_assert(rfl::always_false_v<SourceArrayType>,
|
||||
"Unsupported time unit.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<arrow::Array> res;
|
||||
const auto status = builder.Finish(&res);
|
||||
return Ref<arrow::TimestampArray>::make(
|
||||
std::static_pointer_cast<arrow::TimestampArray>(res));
|
||||
}
|
||||
};
|
||||
|
||||
template <internal::StringLiteral _format>
|
||||
struct ArrowTypes<Timestamp<_format>, SerializationType::csv> {
|
||||
using ArrayType = arrow::TimestampArray;
|
||||
using BuilderType = arrow::StringBuilder;
|
||||
|
||||
static auto data_type() { return arrow::timestamp(arrow::TimeUnit::MILLI); }
|
||||
|
||||
static void add_to_builder(const Timestamp<_format>& _val,
|
||||
BuilderType* _builder) {
|
||||
const auto status = _builder->Append(_val.str());
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<Timestamp<_format>,
|
||||
SerializationType::parquet>::get_array(_arr);
|
||||
}
|
||||
|
||||
static Result<Timestamp<_format>> get_value(const Ref<ArrayType>& _chunk,
|
||||
const int64_t _ix) {
|
||||
return ArrowTypes<Timestamp<_format>,
|
||||
SerializationType::parquet>::get_value(_chunk, _ix);
|
||||
}
|
||||
|
||||
static auto make_builder() { return BuilderType(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
requires internal::has_reflection_type_v<T>
|
||||
struct ArrowTypes<T, _s> {
|
||||
using ArrayType =
|
||||
typename ArrowTypes<typename T::ReflectionType, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<typename T::ReflectionType, _s>::BuilderType;
|
||||
|
||||
static auto data_type() {
|
||||
return ArrowTypes<typename T::ReflectionType, _s>::data_type();
|
||||
}
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
ArrowTypes<typename T::ReflectionType, _s>::add_to_builder(
|
||||
_val.reflection(), _builder);
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<typename T::ReflectionType, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static Result<T> get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<typename T::ReflectionType>,
|
||||
_s>::get_value(_chunk, _ix)
|
||||
.and_then([](const auto& _v) -> Result<T> {
|
||||
try {
|
||||
return T(_v);
|
||||
} catch (const std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static auto make_builder() {
|
||||
return ArrowTypes<typename T::ReflectionType, _s>::make_builder();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowTypes<std::optional<T>, _s> {
|
||||
using ArrayType = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<std::remove_cvref_t<T>, _s>::BuilderType;
|
||||
|
||||
static auto data_type() { return ArrowTypes<T, _s>::data_type(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
if (_val) {
|
||||
ArrowTypes<T, _s>::add_to_builder(*_val, _builder);
|
||||
} else {
|
||||
const auto status = _builder->AppendNull();
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static auto get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<T>, _s>::get_value(_chunk, _ix)
|
||||
.transform([](const auto& _v) { return std::make_optional<T>(_v); });
|
||||
}
|
||||
|
||||
static auto make_builder() { return ArrowTypes<T, _s>::make_builder(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowTypes<std::shared_ptr<T>, _s> {
|
||||
using ArrayType = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<std::remove_cvref_t<T>, _s>::BuilderType;
|
||||
|
||||
static auto data_type() { return ArrowTypes<T, _s>::data_type(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
if (_val) {
|
||||
ArrowTypes<T, _s>::add_to_builder(*_val, _builder);
|
||||
} else {
|
||||
const auto status = _builder->AppendNull();
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static auto get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<T>, _s>::get_value(_chunk, _ix)
|
||||
.transform([](const auto& _v) { return std::make_shared<T>(_v); });
|
||||
}
|
||||
|
||||
static auto make_builder() { return ArrowTypes<T, _s>::make_builder(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowTypes<std::unique_ptr<T>, _s> {
|
||||
using ArrayType = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<std::remove_cvref_t<T>, _s>::BuilderType;
|
||||
|
||||
static auto data_type() { return ArrowTypes<T, _s>::data_type(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
if (_val) {
|
||||
ArrowTypes<T, _s>::add_to_builder(*_val, _builder);
|
||||
} else {
|
||||
const auto status = _builder->AppendNull();
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static auto get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<T>, _s>::get_value(_chunk, _ix)
|
||||
.transform([](const auto& _v) { return std::make_unique<T>(_v); });
|
||||
}
|
||||
|
||||
static auto make_builder() { return ArrowTypes<T, _s>::make_builder(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowTypes<Box<T>, _s> {
|
||||
using ArrayType = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<std::remove_cvref_t<T>, _s>::BuilderType;
|
||||
|
||||
static auto data_type() { return ArrowTypes<T, _s>::data_type(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
ArrowTypes<T, _s>::add_to_builder(*_val, _builder);
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static auto get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<T>, _s>::get_value(_chunk, _ix)
|
||||
.transform([](const auto& _v) { return Box<T>::make(_v); });
|
||||
}
|
||||
|
||||
static auto make_builder() { return ArrowTypes<T, _s>::make_builder(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowTypes<Ref<T>, _s> {
|
||||
using ArrayType = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<std::remove_cvref_t<T>, _s>::BuilderType;
|
||||
|
||||
static auto data_type() { return ArrowTypes<T, _s>::data_type(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
ArrowTypes<T, _s>::add_to_builder(*_val, _builder);
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static auto get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<T>, _s>::get_value(_chunk, _ix)
|
||||
.transform([](const auto& _v) { return Ref<T>::make(_v); });
|
||||
}
|
||||
|
||||
static auto make_builder() { return ArrowTypes<T, _s>::make_builder(); }
|
||||
};
|
||||
|
||||
template <internal::StringLiteral _name, class T, SerializationType _s>
|
||||
struct ArrowTypes<Rename<_name, T>, _s> {
|
||||
using ArrayType = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
using BuilderType =
|
||||
typename ArrowTypes<std::remove_cvref_t<T>, _s>::BuilderType;
|
||||
|
||||
static auto data_type() { return ArrowTypes<T, _s>::data_type(); }
|
||||
|
||||
static void add_to_builder(const auto& _val, BuilderType* _builder) {
|
||||
ArrowTypes<T, _s>::add_to_builder(_val.value(), _builder);
|
||||
}
|
||||
|
||||
static Result<Ref<ArrayType>> get_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr);
|
||||
}
|
||||
|
||||
static auto get_value(const Ref<ArrayType>& _chunk, const int64_t _ix) {
|
||||
return ArrowTypes<std::remove_cvref_t<T>, _s>::get_value(_chunk, _ix)
|
||||
.transform([](const auto& _v) { return Rename<_name, T>(_v); });
|
||||
}
|
||||
|
||||
static auto make_builder() { return ArrowTypes<T, _s>::make_builder(); }
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s, class SourceArrayType>
|
||||
Result<Ref<typename ArrowTypes<T, _s>::ArrayType>>
|
||||
transform_numerical_array_impl(
|
||||
const std::shared_ptr<SourceArrayType>& _arr) noexcept {
|
||||
if (!_arr) {
|
||||
return error(
|
||||
"transform_numerical_array_impl: std::shared_ptr not set. This is a "
|
||||
"bug, please report.");
|
||||
}
|
||||
|
||||
auto builder = ArrowTypes<T, _s>::make_builder();
|
||||
|
||||
for (int64_t i = 0; i < _arr->length(); ++i) {
|
||||
if (_arr->IsNull(i)) {
|
||||
const auto status = builder.AppendNull();
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
} else {
|
||||
const auto status = builder.Append(static_cast<T>(_arr->Value(i)));
|
||||
if (!status.ok()) {
|
||||
return error(status.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using TargetArrayType = typename ArrowTypes<T, _s>::ArrayType;
|
||||
|
||||
std::shared_ptr<arrow::Array> res;
|
||||
const auto status = builder.Finish(&res);
|
||||
return Ref<TargetArrayType>::make(
|
||||
std::static_pointer_cast<TargetArrayType>(res));
|
||||
}
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
Result<Ref<typename ArrowTypes<T, _s>::ArrayType>> transform_numerical_array(
|
||||
const std::shared_ptr<arrow::Array>& _arr) noexcept {
|
||||
if (!_arr) {
|
||||
return error(
|
||||
"Could not transform the numerical array. std::shared_ptr not set.");
|
||||
}
|
||||
|
||||
using ArrayType = typename ArrowTypes<T, _s>::ArrayType;
|
||||
|
||||
if (_arr->type()->Equals(ArrowTypes<T, _s>::data_type())) {
|
||||
return Ref<ArrayType>::make(std::static_pointer_cast<ArrayType>(_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<uint8_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<uint8_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<uint16_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<uint16_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<uint32_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<uint32_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<uint64_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<uint64_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<int8_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<int8_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<int16_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<int16_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<int32_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<int32_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<int64_t, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<int64_t, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<float, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<float, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else if (_arr->type()->Equals(ArrowTypes<double, _s>::data_type())) {
|
||||
return transform_numerical_array_impl<T, _s>(
|
||||
std::static_pointer_cast<typename ArrowTypes<double, _s>::ArrayType>(
|
||||
_arr));
|
||||
|
||||
} else {
|
||||
return error("Expected numerical array, got " + _arr->type()->ToString() +
|
||||
".");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef RFL_PARSING_TABULAR_ARROWWRITER_HPP_
|
||||
#define RFL_PARSING_TABULAR_ARROWWRITER_HPP_
|
||||
|
||||
#include <arrow/api.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../../Processors.hpp"
|
||||
#include "../../Tuple.hpp"
|
||||
#include "../../get.hpp"
|
||||
#include "../../named_tuple_t.hpp"
|
||||
#include "../../to_view.hpp"
|
||||
#include "add_to_builder.hpp"
|
||||
#include "make_arrow_builders.hpp"
|
||||
#include "make_arrow_data_types.hpp"
|
||||
#include "make_arrow_schema.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class VecType, SerializationType _s, class... Ps>
|
||||
class ArrowWriter {
|
||||
static_assert(!Processors<Ps...>::add_tags_to_variants_,
|
||||
"rfl::AddTagsToVariants cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::add_namespaced_tags_to_variants_,
|
||||
"rfl::AddNamespacedTagsToVariants cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::all_required_,
|
||||
"rfl::NoOptionals cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::default_if_missing_,
|
||||
"rfl::DefaultIfMissing cannot be used for tabular data.");
|
||||
static_assert(!Processors<Ps...>::no_extra_fields_,
|
||||
"rfl::NoExtraFields cannot be used for tabular data (neither "
|
||||
"can rfl::ExtraFields).");
|
||||
static_assert(!Processors<Ps...>::no_field_names_,
|
||||
"rfl::NoFieldNames cannot be used for tabular data.");
|
||||
|
||||
public:
|
||||
using ValueType = typename std::remove_cvref_t<typename VecType::value_type>;
|
||||
|
||||
ArrowWriter(const size_t _chunksize) : chunksize_(_chunksize) {}
|
||||
|
||||
~ArrowWriter() = default;
|
||||
|
||||
std::shared_ptr<arrow::Table> to_table(const VecType& _data) const {
|
||||
return arrow::Table::Make(
|
||||
make_arrow_schema<named_tuple_t<ValueType, Ps...>, _s>(),
|
||||
to_chunked_arrays(_data));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<arrow::ChunkedArray>> to_chunked_arrays(
|
||||
const VecType& _data) const;
|
||||
|
||||
private:
|
||||
size_t chunksize_;
|
||||
};
|
||||
|
||||
template <class VecType, SerializationType _s, class... Ps>
|
||||
std::vector<std::shared_ptr<arrow::ChunkedArray>>
|
||||
ArrowWriter<VecType, _s, Ps...>::to_chunked_arrays(const VecType& _data) const {
|
||||
using ValueType = typename VecType::value_type;
|
||||
|
||||
auto builders =
|
||||
make_arrow_builders<named_tuple_t<typename VecType::value_type>, _s>();
|
||||
|
||||
constexpr size_t size = tuple_size_v<decltype(builders)>;
|
||||
|
||||
std::vector<std::vector<std::shared_ptr<arrow::Array>>> array_chunks(size);
|
||||
|
||||
auto it = _data.begin();
|
||||
|
||||
while (it != _data.end()) {
|
||||
size_t i = 0;
|
||||
|
||||
for (; it != _data.end() && (i < chunksize_ || chunksize_ == 0);
|
||||
++i, ++it) {
|
||||
const auto view = to_view(*it);
|
||||
|
||||
[&]<int... _is>(const auto& _v, auto* _b,
|
||||
std::integer_sequence<int, _is...>) {
|
||||
(add_to_builder<_s>(*get<_is>(_v), &(_b->template get<_is>())), ...);
|
||||
}(view, &builders, std::make_integer_sequence<int, size>());
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
std::vector<std::shared_ptr<arrow::Array>> chunks(size);
|
||||
|
||||
const auto finish_builder = [](auto* _b, auto* _c) {
|
||||
const auto status = _b->Finish(_c);
|
||||
if (!status.ok()) {
|
||||
throw std::runtime_error(status.message());
|
||||
}
|
||||
};
|
||||
|
||||
[&]<size_t... _is>(auto* _b, auto* _c,
|
||||
std::integer_sequence<size_t, _is...>) {
|
||||
(finish_builder(&_b->template get<_is>(), &_c->at(_is)), ...);
|
||||
}(&builders, &chunks, std::make_integer_sequence<size_t, size>());
|
||||
|
||||
for (size_t j = 0; j < size; ++j) {
|
||||
array_chunks.at(j).emplace_back(std::move(chunks.at(j)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto data_types = make_arrow_data_types<ValueType, _s>();
|
||||
|
||||
return [&]<size_t... _is>(std::integer_sequence<size_t, _is...>) {
|
||||
return std::vector<std::shared_ptr<arrow::ChunkedArray>>(
|
||||
{std::make_shared<arrow::ChunkedArray>(array_chunks.at(_is),
|
||||
std::get<_is>(data_types))...});
|
||||
}(std::make_integer_sequence<size_t, size>());
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifndef RFL_PARSING_TABULAR_CHUNKEDARRAYITERATOR_HPP_
|
||||
#define RFL_PARSING_TABULAR_CHUNKEDARRAYITERATOR_HPP_
|
||||
|
||||
#include <arrow/api.h>
|
||||
|
||||
#include "../../Ref.hpp"
|
||||
#include "../../Result.hpp"
|
||||
#include "../../internal/ptr_cast.hpp"
|
||||
#include "../is_required.hpp"
|
||||
#include "array_t.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
class ChunkedArrayIterator {
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = Result<T>;
|
||||
|
||||
using ArrayType = array_t<T, _s>;
|
||||
|
||||
static ChunkedArrayIterator make(const Ref<arrow::ChunkedArray>& _arr) {
|
||||
return ChunkedArrayIterator(_arr);
|
||||
}
|
||||
|
||||
ChunkedArrayIterator(const Ref<arrow::ChunkedArray>& _arr)
|
||||
: arr_(_arr), chunk_ix_(0), current_chunk_(get_chunk(arr_, 0)), ix_(0) {}
|
||||
|
||||
~ChunkedArrayIterator() = default;
|
||||
|
||||
Result<T> operator*() const noexcept {
|
||||
const bool is_null =
|
||||
current_chunk_
|
||||
.transform([&](const auto& _c) { return _c->IsNull(ix_); })
|
||||
.value_or(false);
|
||||
|
||||
if (is_null) {
|
||||
if constexpr (is_required<T, false>()) {
|
||||
return error("Value cannot be null.");
|
||||
} else {
|
||||
return T();
|
||||
}
|
||||
}
|
||||
|
||||
return current_chunk_.and_then(
|
||||
[&](const auto& _c) { return ArrowTypes<T, _s>::get_value(_c, ix_); });
|
||||
}
|
||||
|
||||
bool end() const noexcept { return chunk_ix_ >= arr_->num_chunks(); }
|
||||
|
||||
ChunkedArrayIterator& operator++() noexcept {
|
||||
if (!current_chunk_) {
|
||||
return *this;
|
||||
}
|
||||
++ix_;
|
||||
if (ix_ >= (*current_chunk_)->length()) {
|
||||
++chunk_ix_;
|
||||
current_chunk_ = get_chunk(arr_, chunk_ix_);
|
||||
ix_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator++(int) noexcept { ++*this; }
|
||||
|
||||
private:
|
||||
static Result<Ref<ArrayType>> get_chunk(const Ref<arrow::ChunkedArray>& _arr,
|
||||
const int _chunk_ix) noexcept {
|
||||
if (_chunk_ix < _arr->num_chunks()) {
|
||||
return ArrowTypes<T, _s>::get_array(_arr->chunk(_chunk_ix));
|
||||
} else {
|
||||
return error("chunk_ix out of bounds.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Ref<arrow::ChunkedArray> arr_;
|
||||
|
||||
int chunk_ix_;
|
||||
|
||||
Result<Ref<ArrayType>> current_chunk_;
|
||||
|
||||
int64_t ix_;
|
||||
};
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef RFL_PARSING_TABULAR_ADD_TO_BUILDER_HPP_
|
||||
#define RFL_PARSING_TABULAR_ADD_TO_BUILDER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../named_tuple_t.hpp"
|
||||
#include "ArrowTypes.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <SerializationType _s, class ValueType, class BuilderType>
|
||||
inline void add_to_builder(const ValueType& _val, BuilderType* _builder) {
|
||||
ArrowTypes<std::remove_cvref_t<ValueType>, _s>::add_to_builder(_val,
|
||||
_builder);
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef RFL_PARSING_TABULAR_ARRAYT_HPP_
|
||||
#define RFL_PARSING_TABULAR_ARRAYT_HPP_
|
||||
|
||||
#include "ArrowTypes.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
using array_t = typename ArrowTypes<std::remove_cvref_t<T>, _s>::ArrayType;
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef RFL_PARSING_TABULAR_MAKEARROWBUILDERS_HPP_
|
||||
#define RFL_PARSING_TABULAR_MAKEARROWBUILDERS_HPP_
|
||||
|
||||
#include <arrow/api.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../named_tuple_t.hpp"
|
||||
#include "ArrowTypes.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
using arrow_builder_t =
|
||||
typename ArrowTypes<std::remove_cvref_t<std::remove_pointer_t<T>>,
|
||||
_s>::BuilderType;
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
struct ArrowBuildersType;
|
||||
|
||||
template <SerializationType _s, class... FieldTypes>
|
||||
struct ArrowBuildersType<NamedTuple<FieldTypes...>, _s> {
|
||||
using Type = Tuple<arrow_builder_t<typename FieldTypes::Type, _s>...>;
|
||||
|
||||
static auto data_types() {
|
||||
return [&]<size_t... _is>(std::integer_sequence<size_t, _is...>) {
|
||||
return std::array<std::shared_ptr<arrow::DataType>,
|
||||
sizeof...(FieldTypes)>(
|
||||
{ArrowTypes<typename FieldTypes::Type, _s>::data_type()...});
|
||||
}(std::make_integer_sequence<size_t, sizeof...(FieldTypes)>());
|
||||
}
|
||||
|
||||
static Type make_builders() {
|
||||
return Type(ArrowTypes<typename FieldTypes::Type, _s>::make_builder()...);
|
||||
}
|
||||
|
||||
static auto schema() {
|
||||
const auto fields =
|
||||
std::vector<std::shared_ptr<arrow::Field>>({arrow::field(
|
||||
typename FieldTypes::Name().str(),
|
||||
ArrowTypes<typename FieldTypes::Type, _s>::data_type())...});
|
||||
return arrow::schema(fields);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
auto make_arrow_builders() {
|
||||
return ArrowBuildersType<std::remove_cvref_t<T>, _s>::make_builders();
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef RFL_PARSING_TABULAR_MAKE_ARROW_DATA_TYPES_HPP_
|
||||
#define RFL_PARSING_TABULAR_MAKE_ARROW_DATA_TYPES_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../named_tuple_t.hpp"
|
||||
#include "make_arrow_builders.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
inline auto make_arrow_data_types() {
|
||||
return ArrowBuildersType<named_tuple_t<std::remove_cvref_t<T>>,
|
||||
_s>::data_types();
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef RFL_PARSING_TABULAR_MAKE_ARROW_SCHEMA_HPP_
|
||||
#define RFL_PARSING_TABULAR_MAKE_ARROW_SCHEMA_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../../named_tuple_t.hpp"
|
||||
#include "make_arrow_builders.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class T, SerializationType _s>
|
||||
inline auto make_arrow_schema() {
|
||||
return ArrowBuildersType<named_tuple_t<std::remove_cvref_t<T>>, _s>::schema();
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef RFL_PARSING_TABULAR_MAKECHUNKEDARRAYITERATORS_HPP_
|
||||
#define RFL_PARSING_TABULAR_MAKECHUNKEDARRAYITERATORS_HPP_
|
||||
|
||||
#include <arrow/api.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../../NamedTuple.hpp"
|
||||
#include "../../Ref.hpp"
|
||||
#include "../../Result.hpp"
|
||||
#include "../../Tuple.hpp"
|
||||
#include "ChunkedArrayIterator.hpp"
|
||||
|
||||
namespace rfl::parsing::tabular {
|
||||
|
||||
template <class NamedTupleType, SerializationType _s>
|
||||
struct MakeChunkedArrayIterators;
|
||||
|
||||
template <SerializationType _s, class... FieldTypes>
|
||||
struct MakeChunkedArrayIterators<NamedTuple<FieldTypes...>, _s> {
|
||||
using TupleType =
|
||||
Tuple<ChunkedArrayIterator<typename FieldTypes::Type, _s>...>;
|
||||
|
||||
Result<TupleType> operator()(const Ref<arrow::Table>& _table) const {
|
||||
const auto get_column =
|
||||
[&](const std::string& _colname) -> Result<Ref<arrow::ChunkedArray>> {
|
||||
const auto col = _table->GetColumnByName(_colname);
|
||||
if (!col) {
|
||||
return error("Column named '" + _colname + "' not found.");
|
||||
}
|
||||
return Ref<arrow::ChunkedArray>::make(col);
|
||||
};
|
||||
|
||||
try {
|
||||
return TupleType(
|
||||
get_column(typename FieldTypes::Name().str())
|
||||
.transform(
|
||||
ChunkedArrayIterator<typename FieldTypes::Type, _s>::make)
|
||||
.value()...);
|
||||
} catch (const std::exception& e) {
|
||||
return error(e.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class NamedTupleType, SerializationType _s>
|
||||
const auto make_chunked_array_iterators =
|
||||
MakeChunkedArrayIterators<NamedTupleType, _s>{};
|
||||
|
||||
} // namespace rfl::parsing::tabular
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef RFL_PARSING_TAGGEDUNIONWRAPPERNOPTR_HPP_
|
||||
#define RFL_PARSING_TAGGEDUNIONWRAPPERNOPTR_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../internal/StringLiteral.hpp"
|
||||
#include "TaggedUnionWrapper.hpp"
|
||||
|
||||
namespace rfl {
|
||||
namespace parsing {
|
||||
|
||||
template <class T>
|
||||
struct tagged_union_wrapper_no_ptr;
|
||||
|
||||
template <class T>
|
||||
struct tagged_union_wrapper_no_ptr {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <class T, class TagType, internal::StringLiteral _discriminator,
|
||||
bool _as_const_pointer>
|
||||
struct tagged_union_wrapper_no_ptr<
|
||||
TaggedUnionWrapperNoFields<T, TagType, _discriminator, _as_const_pointer>> {
|
||||
using Type = TaggedUnionWrapperNoFields<T, TagType, _discriminator, false>;
|
||||
};
|
||||
|
||||
template <class T, class TagType, internal::StringLiteral _discriminator,
|
||||
bool _as_const_pointer>
|
||||
struct tagged_union_wrapper_no_ptr<TaggedUnionWrapperWithFields<
|
||||
T, TagType, _discriminator, _as_const_pointer>> {
|
||||
using Type = TaggedUnionWrapperWithFields<T, TagType, _discriminator, false>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using tagged_union_wrapper_no_ptr_t =
|
||||
typename tagged_union_wrapper_no_ptr<std::remove_cvref_t<T>>::Type;
|
||||
|
||||
} // namespace parsing
|
||||
} // namespace rfl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef RFL_PARSING_TOSINGLEERRORMESSAGE_HPP_
|
||||
#define RFL_PARSING_TOSINGLEERRORMESSAGE_HPP_
|
||||
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../Result.hpp"
|
||||
#include "../internal/strings/strings.hpp"
|
||||
|
||||
namespace rfl::parsing {
|
||||
|
||||
/// Combines a set of errors to a single, readable error message.
|
||||
inline std::string to_single_error_message(
|
||||
std::vector<Error> _errors,
|
||||
std::optional<std::string> _msg_prefix = std::nullopt,
|
||||
size_t _err_limit = 10) {
|
||||
if (_errors.size() == 1) {
|
||||
return _errors[0].what();
|
||||
} else {
|
||||
std::stringstream stream;
|
||||
stream << (_msg_prefix
|
||||
? *_msg_prefix
|
||||
: "Found " + std::to_string(_errors.size()) + " errors:");
|
||||
for (size_t i = 0; i < _errors.size() && i < _err_limit; ++i) {
|
||||
stream << "\n"
|
||||
<< i + 1 << ") "
|
||||
<< internal::strings::replace_all(_errors.at(i).what(), "\n",
|
||||
"\n ");
|
||||
}
|
||||
if (_errors.size() > _err_limit) {
|
||||
stream << "\n...\nMore than " << _err_limit
|
||||
<< " errors occurred, but I am only showing the "
|
||||
"first "
|
||||
<< _err_limit << ".";
|
||||
}
|
||||
return stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rfl::parsing
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user