diff --git a/README.md b/README.md index 14cf64ca..3c80bc70 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,21 @@ The resulting JSON string looks like this: {"first_name":"Homer","last_name":"Simpson","age":45} ``` +You can transform the field names from `snake_case` to `camelCase` like this: + +```cpp +const std::string json_string = + rfl::json::write(homer); +auto homer2 = + rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"firstName":"Homer","lastName":"Simpson","age":45} +``` + Or you can use another format, such as YAML. ```cpp diff --git a/docs/README.md b/docs/README.md index d295a11a..6640a773 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,21 +10,23 @@ 1.3) [Struct flattening](https://github.com/getml/reflect-cpp/blob/main/docs/flatten_structs.md) - For making struct A "inherit" the fields of struct B. -1.4) [The rfl::Field-syntax](https://github.com/getml/reflect-cpp/blob/main/docs/field_syntax.md) - Describes an alternative syntax which requires slightly more effort, but allows for some powerful functionalities. +1.4) [Processors](https://github.com/getml/reflect-cpp/blob/main/docs/processors.md) - For modifying the structs before serialization and deserialization. For instance, processors can be used to transform all field names from `snake_case` to `camelCase`. -1.5) [String literals](https://github.com/getml/reflect-cpp/blob/main/docs/literals.md) - For representing strings that can only assume a limited number of enumerated values. +1.5) [The rfl::Field-syntax](https://github.com/getml/reflect-cpp/blob/main/docs/field_syntax.md) - Describes an alternative syntax which requires slightly more effort, but allows for some powerful functionalities. -1.6) [Enums](https://github.com/getml/reflect-cpp/blob/main/docs/enums.md) - Describes how reflect-cpp handles C++ enums. +1.6) [String literals](https://github.com/getml/reflect-cpp/blob/main/docs/literals.md) - For representing strings that can only assume a limited number of enumerated values. -1.7) [std::variant and rfl::TaggedUnion](https://github.com/getml/reflect-cpp/blob/main/docs/variants_and_tagged_unions.md) - For structs that can be one of several formats. This is the equivalent of an OR statement or a sum type in type theory. +1.7) [Enums](https://github.com/getml/reflect-cpp/blob/main/docs/enums.md) - Describes how reflect-cpp handles C++ enums. -1.8) [rfl::Box and rfl::Ref](https://github.com/getml/reflect-cpp/blob/main/docs/rfl_ref.md) - For defining recursive structures. +1.8) [std::variant and rfl::TaggedUnion](https://github.com/getml/reflect-cpp/blob/main/docs/variants_and_tagged_unions.md) - For structs that can be one of several formats. This is the equivalent of an OR statement or a sum type in type theory. -1.9) [rfl::Timestamp](https://github.com/getml/reflect-cpp/blob/main/docs/timestamps.md) - For serializing and deserializing timestamps. +1.9) [rfl::Box and rfl::Ref](https://github.com/getml/reflect-cpp/blob/main/docs/rfl_ref.md) - For defining recursive structures. -1.10) [rfl::Result](https://github.com/getml/reflect-cpp/blob/main/docs/result.md) - For error handling without exceptions. +1.10) [rfl::Timestamp](https://github.com/getml/reflect-cpp/blob/main/docs/timestamps.md) - For serializing and deserializing timestamps. -1.11) [Standard containers](https://github.com/getml/reflect-cpp/blob/main/docs/standard_containers.md) - Describes how reflect-cpp treats containers in the standard library. +1.11) [rfl::Result](https://github.com/getml/reflect-cpp/blob/main/docs/result.md) - For error handling without exceptions. + +1.12) [Standard containers](https://github.com/getml/reflect-cpp/blob/main/docs/standard_containers.md) - Describes how reflect-cpp treats containers in the standard library. ## 2) Validation diff --git a/docs/custom_parser.md b/docs/custom_parser.md index a0576c85..84e0ec1a 100644 --- a/docs/custom_parser.md +++ b/docs/custom_parser.md @@ -14,16 +14,14 @@ conditions: You can then implement a custom parser for your class like this: ```cpp -namespace rfl { -namespace parsing { +namespace rfl::parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; -} // namespace parsing -} // namespace rfl +} // namespace rfl::parsing ``` ## Example @@ -77,15 +75,13 @@ struct PersonImpl { You then implement the custom parser: ```cpp -namespace rfl { -namespace parsing { +namespace rfl::parsing { -template +template struct Parser - : public CustomParser {}; + : public CustomParser {}; -} // namespace parsing -} // namespace rfl +} // namespace rfl::parsing ``` Now your custom class is fully supported by reflect-cpp. So for instance, you could parse it diff --git a/docs/processors.md b/docs/processors.md new file mode 100644 index 00000000..a6e4c2d7 --- /dev/null +++ b/docs/processors.md @@ -0,0 +1,108 @@ +# Processors + +Processors can be used to apply transformations to struct serialization and deserialization. + +For instance, C++ [usually](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rl-camel) uses `snake_case`, but JSON uses `camelCase`. One way to handle this is `rfl::Rename`, but a more automated way would be to use a *processor*: + +```cpp +struct Person { + std::string first_name; + std::string last_name; + std::vector children; +}; + +const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .age = 45}; + +const auto json_string = + rfl::json::write(homer); + +const auto homer2 = + rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"firstName":"Homer","lastName":"Simpson","age":45} +``` + +If you want `PascalCase` instead, you can use the appropriate processor: + +```cpp +const auto json_string = + rfl::json::write(homer); + +const auto homer2 = + rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"FirstName":"Homer","LastName":"Simpson","Age":45} +``` + +It is also possible to add the struct name as an addtional field, like this: + +```cpp +const auto json_string = + rfl::json::write>(homer); + +const auto homer2 = + rfl::json::read>(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"type":"Person","first_name":"Homer","last_name":"Simpson","age":45} +``` + +You can also combine several processors: + +It is also possible to add the struct name as an addtional field, like this: + +```cpp +const auto json_string = + rfl::json::write>(homer); + +const auto homer2 = + rfl::json::read>(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"type":"Person","firstName":"Homer","lastName":"Simpson","age":45} +``` + +When you have several processors, it is probably more convenient to combine them like this: + +```cpp +using Processors = rfl::Processors< + rfl::SnakeCaseToCamelCase, rfl::AddStructName<"type">>; + +const auto json_string = rfl::json::write(homer); + +const auto homer2 = rfl::json::read(json_string).value(); +``` + +The resulting JSON string looks like this: + +```json +{"type":"Person","firstName":"Homer","lastName":"Simpson","age":45} +``` + +## Writing your own processors + +In principle, writing your own processors is not very difficult. You need to define a struct, which takes has a static method called `process` taking a named tuple as an input and then returning a modified named tuple. The `process` method should accept the type of the original struct as a template parameter. + +```cpp +struct MyOwnProcessor { + template + static auto process(auto&& _named_tuple) {...} +}; +``` \ No newline at end of file diff --git a/docs/structs.md b/docs/structs.md index ec984863..17c79d5b 100644 --- a/docs/structs.md +++ b/docs/structs.md @@ -18,7 +18,7 @@ const auto bart = Person{.first_name = "Bart", .children = std::vector()}; ``` -JSON uses Hungarian case, but C++ uses snake case, so you might want to rename your fields: +JSON uses camel case, but C++ uses snake case, so you might want to rename your fields: ```cpp struct Person { diff --git a/include/rfl.hpp b/include/rfl.hpp index 183f2c70..705e47bf 100644 --- a/include/rfl.hpp +++ b/include/rfl.hpp @@ -7,6 +7,7 @@ #pragma warning(disable : 4101) #endif +#include "rfl/AddStructName.hpp" #include "rfl/AllOf.hpp" #include "rfl/AnyOf.hpp" #include "rfl/Attribute.hpp" @@ -19,9 +20,12 @@ #include "rfl/OneOf.hpp" #include "rfl/Pattern.hpp" #include "rfl/PatternValidator.hpp" +#include "rfl/Processors.hpp" #include "rfl/Ref.hpp" #include "rfl/Rename.hpp" #include "rfl/Size.hpp" +#include "rfl/SnakeCaseToCamelCase.hpp" +#include "rfl/SnakeCaseToPascalCase.hpp" #include "rfl/TaggedUnion.hpp" #include "rfl/Timestamp.hpp" #include "rfl/Validator.hpp" diff --git a/include/rfl/AddStructName.hpp b/include/rfl/AddStructName.hpp new file mode 100644 index 00000000..7a2272f1 --- /dev/null +++ b/include/rfl/AddStructName.hpp @@ -0,0 +1,32 @@ +#ifndef RFL_ADDSTRUCTNAME_HPP_ +#define RFL_ADDSTRUCTNAME_HPP_ + +#include + +#include "Field.hpp" +#include "Literal.hpp" +#include "internal/StringLiteral.hpp" +#include "internal/get_type_name.hpp" +#include "internal/remove_namespaces.hpp" +#include "make_named_tuple.hpp" + +namespace rfl { + +template +struct AddStructName { + /// Adds the name of the struct as a new field. + template + static auto process(auto&& _view) { + using LiteralType = Literal< + internal::remove_namespaces()>()>; + using FieldType = Field; + const auto add_new_field = [](auto&&... _fields) { + return make_named_tuple(FieldType(LiteralType()), std::move(_fields)...); + }; + return std::apply(add_new_field, std::move(_view.fields())); + } +}; + +} // namespace rfl + +#endif diff --git a/include/rfl/Processors.hpp b/include/rfl/Processors.hpp new file mode 100644 index 00000000..879e2220 --- /dev/null +++ b/include/rfl/Processors.hpp @@ -0,0 +1,28 @@ +#ifndef RFL_INTERNAL_PROCESSORS_HPP_ +#define RFL_INTERNAL_PROCESSORS_HPP_ + +namespace rfl { + +template +struct Processors; + +template <> +struct Processors<> { + template + static auto process(NamedTupleType&& _named_tuple) { + return _named_tuple; + } +}; + +template +struct Processors { + template + static auto process(NamedTupleType&& _named_tuple) { + return Processors::template process( + Head::template process(std::move(_named_tuple))); + } +}; + +} // namespace rfl + +#endif diff --git a/include/rfl/SnakeCaseToCamelCase.hpp b/include/rfl/SnakeCaseToCamelCase.hpp new file mode 100644 index 00000000..9b8dbf12 --- /dev/null +++ b/include/rfl/SnakeCaseToCamelCase.hpp @@ -0,0 +1,38 @@ +#ifndef RFL_SNAKECASETOCAMELCASE_HPP_ +#define RFL_SNAKECASETOCAMELCASE_HPP_ + +#include "Field.hpp" +#include "internal/transform_snake_case.hpp" + +namespace rfl { + +struct SnakeCaseToCamelCase { + public: + /// Replaces all instances of snake_case field names with camelCase. + template + static auto process(auto&& _named_tuple) { + const auto handle_one = [](FieldType&& _f) { + if constexpr (FieldType::name() != "xml_content") { + return handle_one_field(std::move(_f)); + } else { + return std::move(_f); + } + }; + return _named_tuple.transform(handle_one); + } + + private: + /// Applies the logic to a single field. + template + static auto handle_one_field(FieldType&& _f) { + using NewFieldType = + Field(), + typename FieldType::Type>; + return NewFieldType(_f.value()); + } +}; + +} // namespace rfl + +#endif diff --git a/include/rfl/SnakeCaseToPascalCase.hpp b/include/rfl/SnakeCaseToPascalCase.hpp new file mode 100644 index 00000000..9dea9758 --- /dev/null +++ b/include/rfl/SnakeCaseToPascalCase.hpp @@ -0,0 +1,38 @@ +#ifndef RFL_SNAKECASETOPASCALCASE_HPP_ +#define RFL_SNAKECASETOPASCALCASE_HPP_ + +#include "Field.hpp" +#include "internal/transform_snake_case.hpp" + +namespace rfl { + +struct SnakeCaseToPascalCase { + public: + /// Replaces all instances of snake_case field names with PascalCase. + template + static auto process(auto&& _named_tuple) { + const auto handle_one = [](FieldType&& _f) { + if constexpr (FieldType::name() != "xml_content") { + return handle_one_field(std::move(_f)); + } else { + return std::move(_f); + } + }; + return _named_tuple.transform(handle_one); + } + + private: + /// Applies the logic to a single field. + template + static auto handle_one_field(FieldType&& _f) { + using NewFieldType = + Field(), + typename FieldType::Type>; + return NewFieldType(_f.value()); + } +}; + +} // namespace rfl + +#endif diff --git a/include/rfl/bson.hpp b/include/rfl/bson.hpp index f0b11d65..29ca2c5e 100644 --- a/include/rfl/bson.hpp +++ b/include/rfl/bson.hpp @@ -1,6 +1,7 @@ #ifndef RFL_BSON_HPP_ #define RFL_BSON_HPP_ +#include "../rfl.hpp" #include "bson/Parser.hpp" #include "bson/Reader.hpp" #include "bson/Writer.hpp" diff --git a/include/rfl/bson/Parser.hpp b/include/rfl/bson/Parser.hpp index fc8deb31..bd6b871e 100644 --- a/include/rfl/bson/Parser.hpp +++ b/include/rfl/bson/Parser.hpp @@ -8,8 +8,8 @@ namespace rfl { namespace bson { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; } } // namespace rfl diff --git a/include/rfl/bson/load.hpp b/include/rfl/bson/load.hpp index d13a390b..05cd9b78 100644 --- a/include/rfl/bson/load.hpp +++ b/include/rfl/bson/load.hpp @@ -8,9 +8,11 @@ namespace rfl { namespace bson { -template +template Result load(const std::string& _fname) { - const auto read_bytes = [](const auto& _bytes) { return read(_bytes); }; + const auto read_bytes = [](const auto& _bytes) { + return read(_bytes); + }; return rfl::io::load_bytes(_fname).and_then(read_bytes); } diff --git a/include/rfl/bson/read.hpp b/include/rfl/bson/read.hpp index 46bee733..2ee5bc37 100644 --- a/include/rfl/bson/read.hpp +++ b/include/rfl/bson/read.hpp @@ -6,6 +6,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/wrap_in_rfl_array_t.hpp" #include "Parser.hpp" #include "Reader.hpp" @@ -17,41 +18,41 @@ using InputObjectType = typename Reader::InputObjectType; using InputVarType = typename Reader::InputVarType; /// Parses an object from a BSON var. -template +template Result> read(const InputVarType& _obj) { const auto r = Reader(); - return Parser::read(r, _obj); + return Parser>::read(r, _obj); } /// Parses an BSON object using reflection. -template +template auto read(const uint8_t* _bytes, const size_t _size) { Reader::BSONValue value; value.val_.value.v_doc.data_len = static_cast(_size); value.val_.value.v_doc.data = const_cast(_bytes); value.val_.value_type = BSON_TYPE_DOCUMENT; auto doc = InputVarType{&value}; - return read(doc); + return read(doc); } /// Parses an BSON object using reflection. -template +template auto read(const char* _bytes, const size_t _size) { - return read(reinterpret_cast(_bytes), _size); + return read(reinterpret_cast(_bytes), _size); } /// Parses an object from BSON using reflection. -template +template auto read(const std::vector& _bytes) { - return read(_bytes.data(), _bytes.size()); + return read(_bytes.data(), _bytes.size()); } /// Parses an object from a stream. -template +template auto read(std::istream& _stream) { std::istreambuf_iterator begin(_stream), end; auto bytes = std::vector(begin, end); - return read(bytes.data(), bytes.size()); + return read(bytes.data(), bytes.size()); } } // namespace bson diff --git a/include/rfl/bson/save.hpp b/include/rfl/bson/save.hpp index a703bd01..3ebbedad 100644 --- a/include/rfl/bson/save.hpp +++ b/include/rfl/bson/save.hpp @@ -12,10 +12,10 @@ namespace rfl { namespace bson { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream); + return write(_obj, _stream); }; return rfl::io::save_bytes(_fname, _obj, write_func); } diff --git a/include/rfl/bson/write.hpp b/include/rfl/bson/write.hpp index 6bb82482..a22e7503 100644 --- a/include/rfl/bson/write.hpp +++ b/include/rfl/bson/write.hpp @@ -8,6 +8,7 @@ #include #include +#include "../Processors.hpp" #include "../parsing/Parent.hpp" #include "Parser.hpp" @@ -16,8 +17,9 @@ namespace bson { /// Returns BSON bytes. Careful: It is the responsibility of the caller to call /// bson_free on the returned pointer. -template -std::pair to_buffer(const T& _obj) noexcept { +template +std::pair to_buffer(const auto& _obj) noexcept { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; bson_t* doc = nullptr; uint8_t* buf = nullptr; @@ -26,7 +28,8 @@ std::pair to_buffer(const T& _obj) noexcept { bson_writer_new(&buf, &buflen, 0, bson_realloc_ctx, NULL); bson_writer_begin(bson_writer, &doc); const auto rfl_writer = Writer(doc); - Parser::write(rfl_writer, _obj, typename ParentType::Root{}); + Parser>::write(rfl_writer, _obj, + typename ParentType::Root{}); bson_writer_end(bson_writer); const auto len = bson_writer_get_length(bson_writer); bson_writer_destroy(bson_writer); @@ -34,9 +37,9 @@ std::pair to_buffer(const T& _obj) noexcept { } /// Returns BSON bytes. -template -std::vector write(const T& _obj) noexcept { - auto [buf, len] = to_buffer(_obj); +template +std::vector write(const auto& _obj) noexcept { + auto [buf, len] = to_buffer(_obj); const auto result = std::vector(reinterpret_cast(buf), reinterpret_cast(buf) + len); bson_free(buf); @@ -44,9 +47,9 @@ std::vector write(const T& _obj) noexcept { } /// Writes a BSON into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream) noexcept { - auto [buf, len] = to_buffer(_obj); +template +std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept { + auto [buf, len] = to_buffer(_obj); _stream.write(reinterpret_cast(buf), len); bson_free(buf); return _stream; diff --git a/include/rfl/cbor.hpp b/include/rfl/cbor.hpp index 621c8270..e5396175 100644 --- a/include/rfl/cbor.hpp +++ b/include/rfl/cbor.hpp @@ -1,6 +1,7 @@ #ifndef RFL_CBOR_HPP_ #define RFL_CBOR_HPP_ +#include "../rfl.hpp" #include "cbor/Parser.hpp" #include "cbor/Reader.hpp" #include "cbor/Writer.hpp" diff --git a/include/rfl/cbor/Parser.hpp b/include/rfl/cbor/Parser.hpp index c82a73cf..811978d6 100644 --- a/include/rfl/cbor/Parser.hpp +++ b/include/rfl/cbor/Parser.hpp @@ -11,21 +11,23 @@ namespace parsing { /// CBOR requires us to explicitly set the number of fields in advance. Because /// of that, we require all of the fields and then set them to nullptr, if /// necessary. -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, + ProcessorsType> : public NamedTupleParser { + /*_all_required=*/true, ProcessorsType, + FieldTypes...> { }; -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, ProcessorsType> : public TupleParser { + /*_all_required=*/true, ProcessorsType, Ts...> { }; } // namespace parsing @@ -34,8 +36,8 @@ struct Parser> namespace rfl { namespace cbor { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; } } // namespace rfl diff --git a/include/rfl/cbor/load.hpp b/include/rfl/cbor/load.hpp index fc04f294..574ea003 100644 --- a/include/rfl/cbor/load.hpp +++ b/include/rfl/cbor/load.hpp @@ -1,6 +1,7 @@ #ifndef RFL_CBOR_LOAD_HPP_ #define RFL_CBOR_LOAD_HPP_ +#include "../Processors.hpp" #include "../Result.hpp" #include "../io/load_bytes.hpp" #include "read.hpp" @@ -8,9 +9,11 @@ namespace rfl { namespace cbor { -template +template Result load(const std::string& _fname) { - const auto read_bytes = [](const auto& _bytes) { return read(_bytes); }; + const auto read_bytes = [](const auto& _bytes) { + return read(_bytes); + }; return rfl::io::load_bytes(_fname).and_then(read_bytes); } diff --git a/include/rfl/cbor/read.hpp b/include/rfl/cbor/read.hpp index 6a5cf81b..32abb597 100644 --- a/include/rfl/cbor/read.hpp +++ b/include/rfl/cbor/read.hpp @@ -6,6 +6,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/wrap_in_rfl_array_t.hpp" #include "Parser.hpp" #include "Reader.hpp" @@ -17,14 +18,14 @@ using InputObjectType = typename Reader::InputObjectType; using InputVarType = typename Reader::InputVarType; /// Parses an object from a CBOR var. -template +template auto read(const InputVarType& _obj) { const auto r = Reader(); - return Parser::read(r, _obj); + return Parser>::read(r, _obj); } /// Parses an object from CBOR using reflection. -template +template Result> read(const char* _bytes, const size_t _size) { CborParser parser; @@ -32,22 +33,22 @@ Result> read(const char* _bytes, cbor_parser_init(reinterpret_cast(_bytes), _size, 0, &parser, &value); auto doc = InputVarType{&value}; - auto result = read(doc); + auto result = read(doc); return result; } /// Parses an object from CBOR using reflection. -template +template auto read(const std::vector& _bytes) { - return read(_bytes.data(), _bytes.size()); + return read(_bytes.data(), _bytes.size()); } /// Parses an object from a stream. -template +template auto read(std::istream& _stream) { std::istreambuf_iterator begin(_stream), end; auto bytes = std::vector(begin, end); - return read(bytes.data(), bytes.size()); + return read(bytes.data(), bytes.size()); } } // namespace cbor diff --git a/include/rfl/cbor/save.hpp b/include/rfl/cbor/save.hpp index a7a0fa4c..59343e0b 100644 --- a/include/rfl/cbor/save.hpp +++ b/include/rfl/cbor/save.hpp @@ -12,10 +12,10 @@ namespace rfl { namespace cbor { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream); + return write(_obj, _stream); }; return rfl::io::save_bytes(_fname, _obj, write_func); } diff --git a/include/rfl/cbor/write.hpp b/include/rfl/cbor/write.hpp index f2ebdeaa..867f150c 100644 --- a/include/rfl/cbor/write.hpp +++ b/include/rfl/cbor/write.hpp @@ -15,27 +15,29 @@ namespace rfl { namespace cbor { -template -void write_into_buffer(const T& _obj, CborEncoder* _encoder, +template +void write_into_buffer(const auto& _obj, CborEncoder* _encoder, std::vector* _buffer) noexcept { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; cbor_encoder_init(_encoder, reinterpret_cast(_buffer->data()), _buffer->size(), 0); const auto writer = Writer(_encoder); - Parser::write(writer, _obj, typename ParentType::Root{}); + Parser>::write(writer, _obj, + typename ParentType::Root{}); } /// Returns CBOR bytes. -template -std::vector write(const T& _obj) noexcept { +template +std::vector write(const auto& _obj) noexcept { std::vector buffer(4096); CborEncoder encoder; - write_into_buffer(_obj, &encoder, &buffer); + write_into_buffer(_obj, &encoder, &buffer); const auto total_bytes_needed = buffer.size() + cbor_encoder_get_extra_bytes_needed(&encoder); if (total_bytes_needed != buffer.size()) { buffer.resize(total_bytes_needed); - write_into_buffer(_obj, &encoder, &buffer); + write_into_buffer(_obj, &encoder, &buffer); } const auto length = cbor_encoder_get_buffer_size( &encoder, reinterpret_cast(buffer.data())); @@ -44,9 +46,9 @@ std::vector write(const T& _obj) noexcept { } /// Writes a CBOR into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream) noexcept { - auto buffer = write(_obj); +template +std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept { + auto buffer = write(_obj); _stream.write(buffer.data(), buffer.size()); return _stream; } diff --git a/include/rfl/flexbuf.hpp b/include/rfl/flexbuf.hpp index e8ad58a9..b1e73258 100644 --- a/include/rfl/flexbuf.hpp +++ b/include/rfl/flexbuf.hpp @@ -1,6 +1,7 @@ #ifndef RFL_FLEXBUF_HPP_ #define RFL_FLEXBUF_HPP_ +#include "../rfl.hpp" #include "flexbuf/Parser.hpp" #include "flexbuf/Reader.hpp" #include "flexbuf/Writer.hpp" diff --git a/include/rfl/flexbuf/Parser.hpp b/include/rfl/flexbuf/Parser.hpp index f57468e7..dd7020fe 100644 --- a/include/rfl/flexbuf/Parser.hpp +++ b/include/rfl/flexbuf/Parser.hpp @@ -8,8 +8,8 @@ namespace rfl { namespace flexbuf { -template -using Parser = rfl::parsing::Parser; +template +using Parser = parsing::Parser; } } // namespace rfl diff --git a/include/rfl/flexbuf/load.hpp b/include/rfl/flexbuf/load.hpp index bcb17caa..ec0a4e72 100644 --- a/include/rfl/flexbuf/load.hpp +++ b/include/rfl/flexbuf/load.hpp @@ -2,15 +2,17 @@ #define RFL_FLEXBUF_LOAD_HPP_ #include "../Result.hpp" -#include "read.hpp" #include "../io/load_bytes.hpp" +#include "read.hpp" namespace rfl { namespace flexbuf { -template +template Result load(const std::string& _fname) { - const auto read_bytes = [](const auto& _bytes) { return read(_bytes); }; + const auto read_bytes = [](const auto& _bytes) { + return read(_bytes); + }; return rfl::io::load_bytes(_fname).and_then(read_bytes); } diff --git a/include/rfl/flexbuf/read.hpp b/include/rfl/flexbuf/read.hpp index 3a4cf9f3..afdb8050 100644 --- a/include/rfl/flexbuf/read.hpp +++ b/include/rfl/flexbuf/read.hpp @@ -6,6 +6,7 @@ #include #include +#include "../Processors.hpp" #include "../Result.hpp" #include "Parser.hpp" @@ -15,32 +16,32 @@ namespace flexbuf { using InputVarType = typename Reader::InputVarType; /// Parses an object from flexbuf var. -template +template auto read(const InputVarType& _obj) { const auto r = Reader(); - return Parser::read(r, _obj); + return Parser>::read(r, _obj); } /// Parses an object from flexbuf using reflection. -template +template auto read(const char* _bytes, const size_t _size) { const InputVarType root = flexbuffers::GetRoot(reinterpret_cast(_bytes), _size); - return read(root); + return read(root); } /// Parses an object from flexbuf using reflection. -template +template auto read(const std::vector& _bytes) { - return read(_bytes.data(), _bytes.size()); + return read(_bytes.data(), _bytes.size()); } /// Parses an object directly from a stream. -template +template auto read(std::istream& _stream) { std::istreambuf_iterator begin(_stream), end; const auto bytes = std::vector(begin, end); - return read(bytes.data(), bytes.size()); + return read(bytes.data(), bytes.size()); } } // namespace flexbuf diff --git a/include/rfl/flexbuf/save.hpp b/include/rfl/flexbuf/save.hpp index 62f1299e..cc2a8d24 100644 --- a/include/rfl/flexbuf/save.hpp +++ b/include/rfl/flexbuf/save.hpp @@ -6,16 +6,16 @@ #include #include "../Result.hpp" -#include "write.hpp" #include "../io/save_bytes.hpp" +#include "write.hpp" namespace rfl { namespace flexbuf { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream); + return write(_obj, _stream); }; return rfl::io::save_bytes(_fname, _obj, write_func); } diff --git a/include/rfl/flexbuf/write.hpp b/include/rfl/flexbuf/write.hpp index 987e01d7..5409ead8 100644 --- a/include/rfl/flexbuf/write.hpp +++ b/include/rfl/flexbuf/write.hpp @@ -8,6 +8,7 @@ #include #include +#include "../Processors.hpp" #include "../Ref.hpp" #include "../parsing/Parent.hpp" #include "Parser.hpp" @@ -15,28 +16,29 @@ namespace rfl { namespace flexbuf { -template -std::vector to_buffer(const T& _obj) { +template +std::vector to_buffer(const auto& _obj) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; const auto fbb = Ref::make(); auto w = Writer(fbb); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); fbb->Finish(); return fbb->GetBuffer(); } /// Writes an object to flexbuf. -template -std::vector write(const T& _obj) { - const auto buffer = to_buffer(_obj); +template +std::vector write(const auto& _obj) { + const auto buffer = to_buffer(_obj); const auto data = reinterpret_cast(buffer.data()); return std::vector(data, data + buffer.size()); } /// Writes an object to an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream) { - const auto buffer = to_buffer(_obj); +template +std::ostream& write(const auto& _obj, std::ostream& _stream) { + const auto buffer = to_buffer(_obj); const auto data = reinterpret_cast(buffer.data()); _stream.write(data, buffer.size()); return _stream; diff --git a/include/rfl/internal/StringLiteral.hpp b/include/rfl/internal/StringLiteral.hpp index fb5abced..56803ec4 100644 --- a/include/rfl/internal/StringLiteral.hpp +++ b/include/rfl/internal/StringLiteral.hpp @@ -16,6 +16,8 @@ template struct StringLiteral { constexpr StringLiteral(const auto... _chars) : arr_{_chars..., '\0'} {} + constexpr StringLiteral(const std::array _arr) : arr_(_arr) {} + constexpr StringLiteral(const char (&_str)[N]) { std::copy_n(_str, N, std::data(arr_)); } diff --git a/include/rfl/internal/processed_t.hpp b/include/rfl/internal/processed_t.hpp new file mode 100644 index 00000000..69842a59 --- /dev/null +++ b/include/rfl/internal/processed_t.hpp @@ -0,0 +1,27 @@ +#ifndef RFL_INTERNAL_PROCESSED_T_HPP_ +#define RFL_INTERNAL_PROCESSED_T_HPP_ + +#include + +#include "../Processors.hpp" +#include "../named_tuple_t.hpp" + +namespace rfl::internal { + +template +struct Processed; + +template +struct Processed> { + using NamedTupleType = named_tuple_t; + using type = typename std::invoke_result< + decltype(Processors::template process), + NamedTupleType>::type; +}; + +template +using processed_t = typename Processed::type; + +} // namespace rfl::internal + +#endif diff --git a/include/rfl/internal/transform_snake_case.hpp b/include/rfl/internal/transform_snake_case.hpp new file mode 100644 index 00000000..a6a246b0 --- /dev/null +++ b/include/rfl/internal/transform_snake_case.hpp @@ -0,0 +1,38 @@ +#ifndef RFL_INTERNAL_TRANSFORMSNAKECASE_HPP_ +#define RFL_INTERNAL_TRANSFORMSNAKECASE_HPP_ + +#include "StringLiteral.hpp" + +namespace rfl::internal { + +/// Capitalizes a lower-case character. +template +consteval char to_upper() { + if constexpr (c >= 'a' && c <= 'z') { + return c + ('A' - 'a'); + } else { + return c; + } +} + +/// Transforms the field name from snake case to camel case. +template +consteval auto transform_snake_case() { + if constexpr (_i == _name.arr_.size()) { + return StringLiteral(chars...); + } else if constexpr (_name.arr_[_i] == '_') { + return transform_snake_case<_name, true, _i + 1, chars...>(); + } else if constexpr (_name.arr_[_i] == '\0') { + return transform_snake_case<_name, false, _name.arr_.size(), chars...>(); + } else if constexpr (_capitalize) { + return transform_snake_case<_name, false, _i + 1, chars..., + to_upper<_name.arr_[_i]>()>(); + } else { + return transform_snake_case<_name, false, _i + 1, chars..., + _name.arr_[_i]>(); + } +} +} // namespace rfl::internal + +#endif diff --git a/include/rfl/json.hpp b/include/rfl/json.hpp index 34faaa20..cac6b0e2 100644 --- a/include/rfl/json.hpp +++ b/include/rfl/json.hpp @@ -1,6 +1,7 @@ #ifndef RFL_JSON_HPP_ #define RFL_JSON_HPP_ +#include "../rfl.hpp" #include "json/Parser.hpp" #include "json/Reader.hpp" #include "json/Writer.hpp" diff --git a/include/rfl/json/Parser.hpp b/include/rfl/json/Parser.hpp index 013c7f19..9de17554 100644 --- a/include/rfl/json/Parser.hpp +++ b/include/rfl/json/Parser.hpp @@ -1,17 +1,15 @@ #ifndef RFL_JSON_PARSER_HPP_ #define RFL_JSON_PARSER_HPP_ +#include "../parsing/Parser.hpp" #include "Reader.hpp" #include "Writer.hpp" -#include "../parsing/Parser.hpp" -namespace rfl { -namespace json { +namespace rfl::json { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; -} -} // namespace rfl +} // namespace rfl::json #endif diff --git a/include/rfl/json/load.hpp b/include/rfl/json/load.hpp index 9aa89db4..5f7e8f4f 100644 --- a/include/rfl/json/load.hpp +++ b/include/rfl/json/load.hpp @@ -8,9 +8,11 @@ namespace rfl { namespace json { -template +template Result load(const std::string& _fname) { - const auto read_string = [](const auto& _str) { return read(_str); }; + const auto read_string = [](const auto& _str) { + return read(_str); + }; return rfl::io::load_string(_fname).and_then(read_string); } diff --git a/include/rfl/json/read.hpp b/include/rfl/json/read.hpp index 89877bbd..4e29577d 100644 --- a/include/rfl/json/read.hpp +++ b/include/rfl/json/read.hpp @@ -6,6 +6,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/wrap_in_rfl_array_t.hpp" #include "Parser.hpp" #include "Reader.hpp" @@ -17,14 +18,14 @@ using InputObjectType = typename Reader::InputObjectType; using InputVarType = typename Reader::InputVarType; /// Parses an object from a JSON var. -template +template auto read(const InputVarType& _obj) { const auto r = Reader(); - return Parser::read(r, _obj); + return Parser>::read(r, _obj); } /// Parses an object from JSON using reflection. -template +template Result> read(const std::string& _json_str) { using PtrType = std::unique_ptr; yyjson_doc* doc = yyjson_read(_json_str.c_str(), _json_str.size(), 0); @@ -32,15 +33,15 @@ Result> read(const std::string& _json_str) { return Error("Could not parse document!"); } const auto ptr = PtrType(doc, yyjson_doc_free); - return read(InputVarType(yyjson_doc_get_root(doc))); + return read(InputVarType(yyjson_doc_get_root(doc))); } /// Parses an object from a stringstream. -template +template auto read(std::istream& _stream) { const auto json_str = std::string(std::istreambuf_iterator(_stream), std::istreambuf_iterator()); - return read(json_str); + return read(json_str); } } // namespace json diff --git a/include/rfl/json/save.hpp b/include/rfl/json/save.hpp index c2ac2eb9..9875df89 100644 --- a/include/rfl/json/save.hpp +++ b/include/rfl/json/save.hpp @@ -14,11 +14,11 @@ namespace rfl { namespace json { -template -Result save(const std::string& _fname, const T& _obj, +template +Result save(const std::string& _fname, const auto& _obj, const yyjson_write_flag _flag = 0) { const auto write_func = [_flag](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream, _flag); + return write(_obj, _stream, _flag); }; return rfl::io::save_string(_fname, _obj, write_func); } diff --git a/include/rfl/json/to_schema.hpp b/include/rfl/json/to_schema.hpp index b9dc4286..37ff2d58 100644 --- a/include/rfl/json/to_schema.hpp +++ b/include/rfl/json/to_schema.hpp @@ -9,6 +9,7 @@ #include #include "../Literal.hpp" +#include "../Processors.hpp" #include "../parsing/schema/Type.hpp" #include "../parsing/schema/ValidationType.hpp" #include "../parsing/schema/make.hpp" @@ -232,14 +233,15 @@ template struct TypeHelper {}; template -struct TypeHelper > { +struct TypeHelper> { using JSONSchemaType = std::variant...>; }; /// Returns the JSON schema for a class. -template +template std::string to_schema(const yyjson_write_flag _flag = 0) { - const auto internal_schema = parsing::schema::make(); + const auto internal_schema = + parsing::schema::make>(); auto definitions = std::map(); for (const auto& [k, v] : internal_schema.definitions_) { definitions[k] = type_to_json_schema_type(v); diff --git a/include/rfl/json/write.hpp b/include/rfl/json/write.hpp index f9900a3d..8f5bc50e 100644 --- a/include/rfl/json/write.hpp +++ b/include/rfl/json/write.hpp @@ -7,6 +7,7 @@ #include #include +#include "../Processors.hpp" #include "../parsing/Parent.hpp" #include "Parser.hpp" @@ -17,11 +18,12 @@ namespace json { inline constexpr yyjson_write_flag pretty = YYJSON_WRITE_PRETTY; /// Returns a JSON string. -template -std::string write(const T& _obj, const yyjson_write_flag _flag = 0) { +template +std::string write(const auto& _obj, const yyjson_write_flag _flag = 0) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; auto w = Writer(yyjson_mut_doc_new(NULL)); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); const char* json_c_str = yyjson_mut_write(w.doc_, _flag, NULL); const auto json_str = std::string(json_c_str); free((void*)json_c_str); @@ -30,12 +32,13 @@ std::string write(const T& _obj, const yyjson_write_flag _flag = 0) { } /// Writes a JSON into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream, +template +std::ostream& write(const auto& _obj, std::ostream& _stream, const yyjson_write_flag _flag = 0) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; auto w = Writer(yyjson_mut_doc_new(NULL)); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); const char* json_c_str = yyjson_mut_write(w.doc_, _flag, NULL); _stream << json_c_str; free((void*)json_c_str); diff --git a/include/rfl/msgpack.hpp b/include/rfl/msgpack.hpp index 3e006d17..45e8c663 100644 --- a/include/rfl/msgpack.hpp +++ b/include/rfl/msgpack.hpp @@ -1,6 +1,7 @@ #ifndef RFL_MSGPACK_HPP_ #define RFL_MSGPACK_HPP_ +#include "../rfl.hpp" #include "msgpack/Parser.hpp" #include "msgpack/Reader.hpp" #include "msgpack/Writer.hpp" diff --git a/include/rfl/msgpack/Parser.hpp b/include/rfl/msgpack/Parser.hpp index 506cca0e..79da146b 100644 --- a/include/rfl/msgpack/Parser.hpp +++ b/include/rfl/msgpack/Parser.hpp @@ -11,21 +11,24 @@ namespace parsing { /// msgpack-c requires us to explicitly set the number of fields in advance. /// Because of that, we require all of the fields and then set them to nullptr, /// if necessary. -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, + ProcessorsType> : public NamedTupleParser { + /*_all_required=*/true, ProcessorsType, + FieldTypes...> { }; -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, + ProcessorsType> : public TupleParser { + /*_all_required=*/true, ProcessorsType, Ts...> { }; } // namespace parsing @@ -34,8 +37,8 @@ struct Parser> namespace rfl { namespace msgpack { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; } } // namespace rfl diff --git a/include/rfl/msgpack/load.hpp b/include/rfl/msgpack/load.hpp index 52d5b5b3..57802571 100644 --- a/include/rfl/msgpack/load.hpp +++ b/include/rfl/msgpack/load.hpp @@ -8,9 +8,11 @@ namespace rfl { namespace msgpack { -template +template Result load(const std::string& _fname) { - const auto read_bytes = [](const auto& _bytes) { return read(_bytes); }; + const auto read_bytes = [](const auto& _bytes) { + return read(_bytes); + }; return rfl::io::load_bytes(_fname).and_then(read_bytes); } diff --git a/include/rfl/msgpack/read.hpp b/include/rfl/msgpack/read.hpp index 18da56ea..0e0b6763 100644 --- a/include/rfl/msgpack/read.hpp +++ b/include/rfl/msgpack/read.hpp @@ -6,6 +6,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/wrap_in_rfl_array_t.hpp" #include "Parser.hpp" #include "Reader.hpp" @@ -17,37 +18,37 @@ using InputObjectType = typename Reader::InputObjectType; using InputVarType = typename Reader::InputVarType; /// Parses an object from a MSGPACK var. -template +template auto read(const InputVarType& _obj) { const auto r = Reader(); - return Parser::read(r, _obj); + return Parser>::read(r, _obj); } /// Parses an object from MSGPACK using reflection. -template +template Result> read(const char* _bytes, const size_t _size) { msgpack_zone mempool; msgpack_zone_init(&mempool, 2048); msgpack_object deserialized; msgpack_unpack(_bytes, _size, NULL, &mempool, &deserialized); - auto r = read(deserialized); + auto r = read(deserialized); msgpack_zone_destroy(&mempool); return r; } /// Parses an object from MSGPACK using reflection. -template +template auto read(const std::vector& _bytes) { - return read(_bytes.data(), _bytes.size()); + return read(_bytes.data(), _bytes.size()); } /// Parses an object from a stream. -template +template auto read(std::istream& _stream) { std::istreambuf_iterator begin(_stream), end; auto bytes = std::vector(begin, end); - return read(bytes.data(), bytes.size()); + return read(bytes.data(), bytes.size()); } } // namespace msgpack diff --git a/include/rfl/msgpack/save.hpp b/include/rfl/msgpack/save.hpp index aeb9d33d..1d2cfe55 100644 --- a/include/rfl/msgpack/save.hpp +++ b/include/rfl/msgpack/save.hpp @@ -12,10 +12,10 @@ namespace rfl { namespace msgpack { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream); + return write(_obj, _stream); }; return rfl::io::save_bytes(_fname, _obj, write_func); } diff --git a/include/rfl/msgpack/write.hpp b/include/rfl/msgpack/write.hpp index 46315415..620ebb94 100644 --- a/include/rfl/msgpack/write.hpp +++ b/include/rfl/msgpack/write.hpp @@ -9,30 +9,32 @@ #include #include +#include "../Processors.hpp" #include "../parsing/Parent.hpp" #include "Parser.hpp" namespace rfl::msgpack { /// Returns msgpack bytes. -template -std::vector write(const T& _obj) noexcept { +template +std::vector write(const auto& _obj) noexcept { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; msgpack_sbuffer sbuf; msgpack_sbuffer_init(&sbuf); msgpack_packer pk; msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); auto w = Writer(&pk); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); auto bytes = std::vector(sbuf.data, sbuf.data + sbuf.size); msgpack_sbuffer_destroy(&sbuf); return bytes; } /// Writes a MSGPACK into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream) noexcept { - auto buffer = write(_obj); +template +std::ostream& write(const auto& _obj, std::ostream& _stream) noexcept { + auto buffer = write(_obj); _stream.write(buffer.data(), buffer.size()); return _stream; } diff --git a/include/rfl/parsing/CustomParser.hpp b/include/rfl/parsing/CustomParser.hpp index fbebd9ab..07996fd3 100644 --- a/include/rfl/parsing/CustomParser.hpp +++ b/include/rfl/parsing/CustomParser.hpp @@ -12,7 +12,8 @@ namespace rfl { namespace parsing { -template +template struct CustomParser { static Result read(const R& _r, const auto& _var) noexcept { const auto to_class = [](auto&& _h) -> Result { @@ -30,20 +31,21 @@ struct CustomParser { return Error(e.what()); } }; - return Parser::read(_r, _var).and_then(to_class); + return Parser::read(_r, _var).and_then( + to_class); } template static auto write(const W& _w, const OriginalClass& _p, const P& _parent) noexcept { - Parser::write(_w, HelperStruct::from_class(_p), - _parent); + Parser::write( + _w, HelperStruct::from_class(_p), _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema( - _definitions); + return Parser, + ProcessorsType>::to_schema(_definitions); } }; diff --git a/include/rfl/parsing/FieldVariantParser.hpp b/include/rfl/parsing/FieldVariantParser.hpp index 34dd8d8d..6a9fe8d9 100644 --- a/include/rfl/parsing/FieldVariantParser.hpp +++ b/include/rfl/parsing/FieldVariantParser.hpp @@ -18,7 +18,7 @@ namespace parsing { /// To be used when all options of the variants are rfl::Field. Essentially, /// this is an externally tagged union. -template +template requires AreReaderAndWriter> struct FieldVariantParser { using FieldVariantType = std::variant; @@ -40,7 +40,8 @@ struct FieldVariantParser { const auto to_result = [&](const auto _obj) -> ResultType { auto field_variant = std::optional>(); const auto reader = - FieldVariantReader(&_r, &field_variant); + FieldVariantReader( + &_r, &field_variant); auto err = _r.read_object(reader, _obj); if (err) { return *err; @@ -67,7 +68,8 @@ struct FieldVariantParser { const auto handle = [&](const auto& _field) { const auto named_tuple = make_named_tuple(internal::to_ptr_field(_field)); using NamedTupleType = std::remove_cvref_t; - Parser::write(_w, named_tuple, _parent); + Parser::write(_w, named_tuple, + _parent); }; std::visit(handle, _v); @@ -77,7 +79,7 @@ struct FieldVariantParser { std::map* _definitions, std::vector _types = {}) { using VariantType = std::variant...>; - return Parser::to_schema(_definitions); + return Parser::to_schema(_definitions); } }; } // namespace parsing diff --git a/include/rfl/parsing/FieldVariantReader.hpp b/include/rfl/parsing/FieldVariantReader.hpp index ed5799e0..275b6a60 100644 --- a/include/rfl/parsing/FieldVariantReader.hpp +++ b/include/rfl/parsing/FieldVariantReader.hpp @@ -13,7 +13,7 @@ namespace rfl::parsing { -template +template class FieldVariantReader { private: using InputVarType = typename R::InputVarType; @@ -55,9 +55,10 @@ class FieldVariantReader { return Error("Could not parse std::variant with field '" + std::string(_disc_value) + "': " + _e.what()); }; - *field_variant_ = Parser::read(*r_, _var) - .transform(to_variant) - .or_else(embellish_error); + *field_variant_ = + Parser::read(*r_, _var) + .transform(to_variant) + .or_else(embellish_error); return; } else { read<_i + 1>(_disc_value, _var); diff --git a/include/rfl/parsing/MapParser.hpp b/include/rfl/parsing/MapParser.hpp index a21593f5..379f889e 100644 --- a/include/rfl/parsing/MapParser.hpp +++ b/include/rfl/parsing/MapParser.hpp @@ -18,7 +18,7 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter struct MapParser { public: @@ -52,23 +52,25 @@ struct MapParser { std::is_floating_point_v) { const auto name = std::to_string(k.reflection()); const auto new_parent = typename ParentType::Object{name, &obj}; - Parser>::write(_w, v, - new_parent); + Parser, ProcessorsType>::write( + _w, v, new_parent); } else { const auto name = k.reflection(); const auto new_parent = typename ParentType::Object{name, &obj}; - Parser>::write(_w, v, - new_parent); + Parser, ProcessorsType>::write( + _w, v, new_parent); } } else if constexpr (std::is_integral_v || std::is_floating_point_v) { const auto name = std::to_string(k); const auto new_parent = typename ParentType::Object{name, &obj}; - Parser>::write(_w, v, new_parent); + Parser, ProcessorsType>::write( + _w, v, new_parent); } else { const auto new_parent = typename ParentType::Object{k, &obj}; - Parser>::write(_w, v, new_parent); + Parser, ProcessorsType>::write( + _w, v, new_parent); } } _w.end_object(&obj); @@ -77,14 +79,15 @@ struct MapParser { static schema::Type to_schema( std::map* _definitions) { return schema::Type{schema::Type::StringMap{Ref::make( - Parser::to_schema(_definitions))}}; + Parser::to_schema(_definitions))}}; } private: static Result make_map(const R& _r, const InputObjectType& _obj) { MapType map; std::vector errors; - const auto map_reader = MapReader(&_r, &map, &errors); + const auto map_reader = + MapReader(&_r, &map, &errors); const auto err = _r.read_object(map_reader, _obj); if (err) { return *err; diff --git a/include/rfl/parsing/MapReader.hpp b/include/rfl/parsing/MapReader.hpp index 8e63fc8e..8c62843a 100644 --- a/include/rfl/parsing/MapReader.hpp +++ b/include/rfl/parsing/MapReader.hpp @@ -12,7 +12,7 @@ namespace rfl::parsing { -template +template class MapReader { private: using InputVarType = typename R::InputVarType; @@ -91,7 +91,8 @@ class MapReader { auto pair = std::make_pair(std::string(_name), std::move(_val)); return make_key(pair); }; - return Parser>::read(*r_, _var) + return Parser, ProcessorsType>::read( + *r_, _var) .and_then(to_pair); } diff --git a/include/rfl/parsing/NamedTupleParser.hpp b/include/rfl/parsing/NamedTupleParser.hpp index 2412bd96..7f012f9d 100644 --- a/include/rfl/parsing/NamedTupleParser.hpp +++ b/include/rfl/parsing/NamedTupleParser.hpp @@ -29,7 +29,7 @@ namespace rfl { namespace parsing { template + class ProcessorsType, class... FieldTypes> requires AreReaderAndWriter> struct NamedTupleParser { using InputObjectType = typename R::InputObjectType; @@ -54,9 +54,10 @@ struct NamedTupleParser { alignas(NamedTuple) unsigned char buf[sizeof(NamedTuple)]; auto ptr = reinterpret_cast*>(buf); - const auto view = rfl::to_view(*ptr); + auto view = rfl::to_view(*ptr); using ViewType = std::remove_cvref_t; - const auto err = Parser::read_view(_r, _var, view); + const auto err = + Parser::read_view(_r, _var, &view); if (err) { return *err; } @@ -66,7 +67,7 @@ struct NamedTupleParser { /// Reads the data into a view. static std::optional read_view( const R& _r, const InputVarType& _var, - const NamedTuple& _view) noexcept { + NamedTuple* _view) noexcept { const auto obj = _r.to_object(_var); if (obj) { return read_object(_r, *obj, _view); @@ -96,9 +97,11 @@ struct NamedTupleParser { if constexpr (_i == size) { return Type{Type::Object{_values}}; } else { - using F = std::tuple_element_t<_i, typename NamedTuple::Fields>; + using F = + std::tuple_element_t<_i, typename NamedTuple::Fields>; _values[std::string(F::name())] = - Parser::to_schema(_definitions); + Parser::to_schema( + _definitions); return to_schema<_i + 1>(_definitions, _values); } }; @@ -121,17 +124,19 @@ struct NamedTupleParser { !is_required()) { if (!is_empty(value)) { if constexpr (internal::is_attribute_v) { - Parser::write(_w, value, - new_parent.as_attribute()); + Parser::write( + _w, value, new_parent.as_attribute()); } else { - Parser::write(_w, value, new_parent); + Parser::write(_w, value, + new_parent); } } } else { if constexpr (internal::is_attribute_v) { - Parser::write(_w, value, new_parent.as_attribute()); + Parser::write( + _w, value, new_parent.as_attribute()); } else { - Parser::write(_w, value, new_parent); + Parser::write(_w, value, new_parent); } } return build_object_recursively<_i + 1>(_w, _tup, _ptr); @@ -154,20 +159,22 @@ struct NamedTupleParser { /// trigger the destructors. template static void call_destructors_where_necessary( - const NamedTupleType& _view, const std::array& _set) { + NamedTupleType* _view, const std::array& _set) { if constexpr (_i < sizeof...(FieldTypes)) { using FieldType = std::tuple_element_t<_i, typename NamedTupleType::Fields>; + using OriginalType = std::remove_cvref_t; using ValueType = std::remove_cvref_t>; if constexpr (!std::is_array_v && + std::is_pointer_v && std::is_destructible_v) { if (std::get<_i>(_set)) { - rfl::get<_i>(_view)->~ValueType(); + rfl::get<_i>(*_view)->~ValueType(); } } else if constexpr (std::is_array_v) { if (std::get<_i>(_set)) { - auto ptr = rfl::get<_i>(_view); + auto ptr = rfl::get<_i>(*_view); call_destructor_on_array(sizeof(*ptr) / sizeof(**ptr), *ptr); } } @@ -208,21 +215,21 @@ struct NamedTupleParser { } } - static std::optional read_object( - const R& _r, const InputObjectType& _obj, - const NamedTupleType& _view) noexcept { + static std::optional read_object(const R& _r, + const InputObjectType& _obj, + NamedTupleType* _view) noexcept { auto found = std::array(); found.fill(false); auto set = std::array(); set.fill(false); std::vector errors; - const auto object_reader = - ViewReader(&_r, &_view, &found, &set, &errors); + const auto object_reader = ViewReader( + &_r, _view, &found, &set, &errors); const auto err = _r.read_object(object_reader, _obj); if (err) { return *err; } - handle_missing_fields(found, _view, &set, &errors); + handle_missing_fields(found, *_view, &set, &errors); if (errors.size() != 0) { call_destructors_where_necessary(_view, set); return to_single_error_message(errors); diff --git a/include/rfl/parsing/Parser_array.hpp b/include/rfl/parsing/Parser_array.hpp index 536f5fa8..713dbc69 100644 --- a/include/rfl/parsing/Parser_array.hpp +++ b/include/rfl/parsing/Parser_array.hpp @@ -15,9 +15,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { public: using InputArrayType = typename R::InputArrayType; using InputVarType = typename R::InputVarType; @@ -55,7 +55,8 @@ struct Parser> { auto arr = ParentType::add_array(_w, _size, _parent); const auto new_parent = typename ParentType::Array{&arr}; for (const auto& e : _arr) { - Parser>::write(_w, e, new_parent); + Parser, ProcessorsType>::write(_w, e, + new_parent); } _w.end_array(&arr); } @@ -65,8 +66,8 @@ struct Parser> { using U = std::remove_cvref_t; return schema::Type{schema::Type::FixedSizeTypedArray{ .size_ = _size, - .type_ = - Ref::make(Parser::to_schema(_definitions))}}; + .type_ = Ref::make( + Parser::to_schema(_definitions))}}; } private: @@ -92,7 +93,7 @@ struct Parser> { template static auto extract_single_field( const R& _r, const std::vector& _vec) noexcept { - return Parser::read(_r, _vec[_i]); + return Parser::read(_r, _vec[_i]); } }; diff --git a/include/rfl/parsing/Parser_base.hpp b/include/rfl/parsing/Parser_base.hpp index 32ef3adf..abc0d0bd 100644 --- a/include/rfl/parsing/Parser_base.hpp +++ b/include/rfl/parsing/Parser_base.hpp @@ -6,7 +6,7 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter struct Parser; diff --git a/include/rfl/parsing/Parser_box.hpp b/include/rfl/parsing/Parser_box.hpp index af209309..89c41eaf 100644 --- a/include/rfl/parsing/Parser_box.hpp +++ b/include/rfl/parsing/Parser_box.hpp @@ -12,27 +12,29 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; static Result> read(const R& _r, const InputVarType& _var) noexcept { const auto to_box = [](auto&& _t) { return Box::make(std::move(_t)); }; - return Parser>::read(_r, _var).transform( - to_box); + return Parser, ProcessorsType>::read(_r, _var) + .transform(to_box); } template static void write(const W& _w, const Box& _box, const P& _parent) noexcept { - Parser>::write(_w, *_box, _parent); + Parser, ProcessorsType>::write(_w, *_box, + _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_c_array.hpp b/include/rfl/parsing/Parser_c_array.hpp index eaeee92a..1d9d2dcb 100644 --- a/include/rfl/parsing/Parser_c_array.hpp +++ b/include/rfl/parsing/Parser_c_array.hpp @@ -16,9 +16,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter -struct Parser { +struct Parser { public: using InputArrayType = typename R::InputArrayType; using InputVarType = typename R::InputVarType; @@ -32,7 +32,7 @@ struct Parser { static Result> read( const R& _r, const InputVarType& _var) noexcept { using StdArray = internal::to_std_array_t; - return Parser::read(_r, _var); + return Parser::read(_r, _var); } template @@ -41,7 +41,8 @@ struct Parser { auto arr = ParentType::add_array(_w, _size, _parent); const auto new_parent = typename ParentType::Array{&arr}; for (const auto& e : _arr) { - Parser>::write(_w, e, new_parent); + Parser, ProcessorsType>::write(_w, e, + new_parent); } _w.end_array(&arr); } @@ -49,7 +50,8 @@ struct Parser { static schema::Type to_schema( std::map* _definitions) { using StdArray = internal::to_std_array_t; - return Parser>::to_schema(_definitions); + return Parser, + ProcessorsType>::to_schema(_definitions); } }; diff --git a/include/rfl/parsing/Parser_default.hpp b/include/rfl/parsing/Parser_default.hpp index 97d5e62d..a3b5b0a7 100644 --- a/include/rfl/parsing/Parser_default.hpp +++ b/include/rfl/parsing/Parser_default.hpp @@ -15,6 +15,7 @@ #include "../internal/is_description.hpp" #include "../internal/is_literal.hpp" #include "../internal/is_validator.hpp" +#include "../internal/processed_t.hpp" #include "../internal/to_ptr_named_tuple.hpp" #include "../type_name_t.hpp" #include "AreReaderAndWriter.hpp" @@ -28,7 +29,7 @@ namespace rfl { namespace parsing { /// Default case - anything that cannot be explicitly matched. -template +template requires AreReaderAndWriter struct Parser { public: @@ -51,9 +52,10 @@ struct Parser { return Error(e.what()); } }; - return Parser::read(_r, _var).and_then(wrap_in_t); + return Parser::read(_r, _var) + .and_then(wrap_in_t); } else if constexpr (std::is_class_v && std::is_aggregate_v) { - return StructReader::read(_r, _var); + return StructReader::read(_r, _var); } else if constexpr (std::is_enum_v) { using StringConverter = internal::enums::StringConverter; return _r.template to_basic_type(_var).and_then( @@ -75,15 +77,18 @@ struct Parser { if constexpr (internal::has_reflection_type_v) { using ReflectionType = std::remove_cvref_t; if constexpr (internal::has_reflection_method_v) { - Parser::write(_w, _var.reflection(), _parent); + Parser::write( + _w, _var.reflection(), _parent); } else { const auto& [r] = _var; - Parser::write(_w, r, _parent); + Parser::write(_w, r, _parent); } } else if constexpr (std::is_class_v && std::is_aggregate_v) { - const auto ptr_named_tuple = internal::to_ptr_named_tuple(_var); + const auto ptr_named_tuple = ProcessorsType::template process( + internal::to_ptr_named_tuple(_var)); using PtrNamedTupleType = std::remove_cvref_t; - Parser::write(_w, ptr_named_tuple, _parent); + Parser::write( + _w, ptr_named_tuple, _parent); } else if constexpr (std::is_enum_v) { using StringConverter = internal::enums::StringConverter; const auto str = StringConverter::enum_to_string(_var); @@ -146,7 +151,8 @@ struct Parser { return make_validated(_definitions); } else if constexpr (internal::has_reflection_type_v) { - return Parser::to_schema(_definitions); + return Parser::to_schema(_definitions); } else { static_assert(rfl::always_false_v, "Unsupported type."); @@ -160,9 +166,9 @@ struct Parser { using Type = schema::Type; return Type{Type::Description{ .description_ = typename U::Content().str(), - .type_ = Ref::make( - Parser>::to_schema( - _definitions))}}; + .type_ = + Ref::make(Parser, + ProcessorsType>::to_schema(_definitions))}}; } template @@ -173,7 +179,8 @@ struct Parser { if constexpr (S::is_flag_enum_) { return Type{Type::String{}}; } else { - return Parser::to_schema(_definitions); + return Parser::to_schema( + _definitions); } } @@ -185,8 +192,9 @@ struct Parser { if (_definitions->find(name) == _definitions->end()) { (*_definitions)[name] = Type{Type::Integer{}}; // Placeholder to avoid infinite loop. + using NamedTupleType = internal::processed_t; (*_definitions)[name] = - Parser>::to_schema(_definitions); + Parser::to_schema(_definitions); } return Type{Type::Reference{name}}; } @@ -199,7 +207,8 @@ struct Parser { using ValidationType = std::remove_cvref_t; return Type{Type::Validated{ .type_ = Ref::make( - Parser::to_schema(_definitions)), + Parser::to_schema( + _definitions)), .validation_ = ValidationType::template to_schema()}}; } diff --git a/include/rfl/parsing/Parser_map_like.hpp b/include/rfl/parsing/Parser_map_like.hpp index 95d42486..04f34e01 100644 --- a/include/rfl/parsing/Parser_map_like.hpp +++ b/include/rfl/parsing/Parser_map_like.hpp @@ -15,16 +15,17 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> - : public MapParser> { +struct Parser, ProcessorsType> + : public MapParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public MapParser> { +struct Parser, ProcessorsType> + : public MapParser, + ProcessorsType> { }; } // namespace parsing diff --git a/include/rfl/parsing/Parser_named_tuple.hpp b/include/rfl/parsing/Parser_named_tuple.hpp index 8f8c44bb..c8c7d7fc 100644 --- a/include/rfl/parsing/Parser_named_tuple.hpp +++ b/include/rfl/parsing/Parser_named_tuple.hpp @@ -7,11 +7,12 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, ProcessorsType> : public NamedTupleParser { + /*_all_required=*/false, ProcessorsType, + FieldTypes...> { }; } // namespace parsing diff --git a/include/rfl/parsing/Parser_optional.hpp b/include/rfl/parsing/Parser_optional.hpp index 16643bbd..c099a025 100644 --- a/include/rfl/parsing/Parser_optional.hpp +++ b/include/rfl/parsing/Parser_optional.hpp @@ -15,9 +15,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -29,8 +29,8 @@ struct Parser> { return std::optional(); } const auto to_opt = [](auto&& _t) { return std::make_optional(_t); }; - return Parser>::read(_r, _var).transform( - to_opt); + return Parser, ProcessorsType>::read(_r, _var) + .transform(to_opt); } template @@ -40,14 +40,15 @@ struct Parser> { ParentType::add_null(_w, _parent); return; } - Parser>::write(_w, *_o, _parent); + Parser, ProcessorsType>::write(_w, *_o, + _parent); } static schema::Type to_schema( std::map* _definitions) { using U = std::remove_cvref_t; - return schema::Type{schema::Type::Optional{ - Ref::make(Parser::to_schema(_definitions))}}; + return schema::Type{schema::Type::Optional{Ref::make( + Parser::to_schema(_definitions))}}; } }; diff --git a/include/rfl/parsing/Parser_pair.hpp b/include/rfl/parsing/Parser_pair.hpp index d33c3934..351090e7 100644 --- a/include/rfl/parsing/Parser_pair.hpp +++ b/include/rfl/parsing/Parser_pair.hpp @@ -14,9 +14,10 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -27,7 +28,8 @@ struct Parser> { return std::make_pair(std::move(std::get<0>(_t)), std::move(std::get<1>(_t))); }; - return Parser>::read(_r, _var) + return Parser, + ProcessorsType>::read(_r, _var) .transform(to_pair); } @@ -35,14 +37,14 @@ struct Parser> { static void write(const W& _w, const std::pair& _p, const P& _parent) noexcept { const auto tup = std::make_tuple(&_p.first, &_p.second); - Parser>::write( - _w, tup, _parent); + Parser, + ProcessorsType>::write(_w, tup, _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema( - _definitions); + return Parser, + ProcessorsType>::to_schema(_definitions); } }; diff --git a/include/rfl/parsing/Parser_ptr.hpp b/include/rfl/parsing/Parser_ptr.hpp index 738d0cca..b7b76eb6 100644 --- a/include/rfl/parsing/Parser_ptr.hpp +++ b/include/rfl/parsing/Parser_ptr.hpp @@ -14,9 +14,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter -struct Parser { +struct Parser { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -39,12 +39,14 @@ struct Parser { ParentType::add_null(_w, _parent); return; } - Parser>::write(_w, *_ptr, _parent); + Parser, ProcessorsType>::write(_w, *_ptr, + _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_ref.hpp b/include/rfl/parsing/Parser_ref.hpp index 441b7412..961ad55d 100644 --- a/include/rfl/parsing/Parser_ref.hpp +++ b/include/rfl/parsing/Parser_ref.hpp @@ -13,27 +13,29 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; static Result> read(const R& _r, const InputVarType& _var) noexcept { const auto to_ref = [&](auto&& _t) { return Ref::make(std::move(_t)); }; - return Parser>::read(_r, _var).transform( - to_ref); + return Parser, ProcessorsType>::read(_r, _var) + .transform(to_ref); } template static void write(const W& _w, const Ref& _ref, const P& _parent) noexcept { - Parser>::write(_w, *_ref, _parent); + Parser, ProcessorsType>::write(_w, *_ref, + _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_reference_wrapper.hpp b/include/rfl/parsing/Parser_reference_wrapper.hpp index 023fe13a..2daadff9 100644 --- a/include/rfl/parsing/Parser_reference_wrapper.hpp +++ b/include/rfl/parsing/Parser_reference_wrapper.hpp @@ -13,9 +13,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -33,12 +33,14 @@ struct Parser> { template static void write(const W& _w, const std::reference_wrapper _ref, const P& _p) noexcept { - Parser>::write(_w, _ref.get(), _p); + Parser, ProcessorsType>::write(_w, _ref.get(), + _p); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_rename.hpp b/include/rfl/parsing/Parser_rename.hpp index 2cc94ab0..641a357b 100644 --- a/include/rfl/parsing/Parser_rename.hpp +++ b/include/rfl/parsing/Parser_rename.hpp @@ -14,9 +14,10 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -25,19 +26,21 @@ struct Parser> { const auto to_rename = [](auto&& _t) { return Rename<_name, T>(std::move(_t)); }; - return Parser>::read(_r, _var).transform( - to_rename); + return Parser, ProcessorsType>::read(_r, _var) + .transform(to_rename); } template static void write(const W& _w, const Rename<_name, T>& _rename, const P& _parent) noexcept { - Parser>::write(_w, _rename.value(), _parent); + Parser, ProcessorsType>::write( + _w, _rename.value(), _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_result.hpp b/include/rfl/parsing/Parser_result.hpp index a7e0dd60..e91b66ac 100644 --- a/include/rfl/parsing/Parser_result.hpp +++ b/include/rfl/parsing/Parser_result.hpp @@ -13,9 +13,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -38,19 +38,21 @@ struct Parser> { }; return Result>( - Parser::read(_r, _var).transform(to_res)); + Parser::read(_r, _var).transform( + to_res)); } template static void write(const W& _w, const Result& _r, const P& _parent) noexcept { const auto write_t = [&](const auto& _t) -> Nothing { - Parser>::write(_w, _t, _parent); + Parser, ProcessorsType>::write(_w, _t, + _parent); return Nothing{}; }; const auto write_err = [&](const auto& _err) -> Nothing { - Parser::write( + Parser::write( _w, ErrorType(make_field<"error">(_err.what())), _parent); return Nothing{}; }; @@ -60,7 +62,8 @@ struct Parser> { static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_rfl_array.hpp b/include/rfl/parsing/Parser_rfl_array.hpp index e3e837f9..604aed69 100644 --- a/include/rfl/parsing/Parser_rfl_array.hpp +++ b/include/rfl/parsing/Parser_rfl_array.hpp @@ -15,9 +15,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { public: using InputArrayType = typename R::InputArrayType; using InputVarType = typename R::InputVarType; @@ -29,18 +29,18 @@ struct Parser> { static Result> read(const R& _r, const InputVarType& _var) noexcept { - return Parser::read(_r, _var); + return Parser::read(_r, _var); } template static void write(const W& _w, const internal::Array& _arr, const P& _parent) noexcept { - Parser::write(_w, _arr.arr_, _parent); + Parser::write(_w, _arr.arr_, _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser::to_schema(_definitions); + return Parser::to_schema(_definitions); } }; diff --git a/include/rfl/parsing/Parser_rfl_variant.hpp b/include/rfl/parsing/Parser_rfl_variant.hpp index 5b85bc02..2fb1a15e 100644 --- a/include/rfl/parsing/Parser_rfl_variant.hpp +++ b/include/rfl/parsing/Parser_rfl_variant.hpp @@ -15,9 +15,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -28,20 +28,22 @@ struct Parser> { return rfl::Variant( std::forward>(_var)); }; - return Parser>::read(_r, _var).transform( - to_rfl_variant); + return Parser, ProcessorsType>::read(_r, + _var) + .transform(to_rfl_variant); } template static void write(const W& _w, const rfl::Variant& _variant, const P& _parent) noexcept { - Parser>::write(_w, _variant.variant(), - _parent); + Parser, ProcessorsType>::write( + _w, _variant.variant(), _parent); } static schema::Type to_schema( std::map* _definitions) { - return Parser>::to_schema(_definitions); + return Parser, ProcessorsType>::to_schema( + _definitions); } }; diff --git a/include/rfl/parsing/Parser_shared_ptr.hpp b/include/rfl/parsing/Parser_shared_ptr.hpp index cd29da63..7af759e1 100644 --- a/include/rfl/parsing/Parser_shared_ptr.hpp +++ b/include/rfl/parsing/Parser_shared_ptr.hpp @@ -14,9 +14,9 @@ namespace rfl::parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -30,8 +30,8 @@ struct Parser> { const auto to_ptr = [](auto&& _t) { return std::make_shared(std::move(_t)); }; - return Parser>::read(_r, _var).transform( - to_ptr); + return Parser, ProcessorsType>::read(_r, _var) + .transform(to_ptr); } template @@ -41,14 +41,15 @@ struct Parser> { ParentType::add_null(_w, _parent); return; } - Parser>::write(_w, *_s, _parent); + Parser, ProcessorsType>::write(_w, *_s, + _parent); } static schema::Type to_schema( std::map* _definitions) { using U = std::remove_cvref_t; - return schema::Type{schema::Type::Optional{ - Ref::make(Parser::to_schema(_definitions))}}; + return schema::Type{schema::Type::Optional{Ref::make( + Parser::to_schema(_definitions))}}; } }; diff --git a/include/rfl/parsing/Parser_string_view.hpp b/include/rfl/parsing/Parser_string_view.hpp index 62cb26fb..59a4dede 100644 --- a/include/rfl/parsing/Parser_string_view.hpp +++ b/include/rfl/parsing/Parser_string_view.hpp @@ -14,9 +14,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter -struct Parser { +struct Parser { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -32,12 +32,13 @@ struct Parser { template static void write(const W& _w, const std::string_view& _str, const P& _p) noexcept { - Parser::write(_w, std::string(_str), _p); + Parser::write(_w, std::string(_str), + _p); } static schema::Type to_schema( std::map* _definitions) { - return Parser::to_schema(_definitions); + return Parser::to_schema(_definitions); } }; diff --git a/include/rfl/parsing/Parser_tagged_union.hpp b/include/rfl/parsing/Parser_tagged_union.hpp index 4d7d9c17..5aa5ea0e 100644 --- a/include/rfl/parsing/Parser_tagged_union.hpp +++ b/include/rfl/parsing/Parser_tagged_union.hpp @@ -16,10 +16,11 @@ namespace rfl { namespace parsing { template + class... AlternativeTypes, class ProcessorsType> requires AreReaderAndWriter> -struct Parser> { +struct Parser, + ProcessorsType> { using ResultType = Result>; public: @@ -56,7 +57,7 @@ struct Parser> { std::map* _definitions) { using VariantType = std::variant), AlternativeTypes>...>; - return Parser::to_schema(_definitions); + return Parser::to_schema(_definitions); } private: @@ -89,7 +90,7 @@ struct Parser> { _discriminator.str() + " '" + _disc_value + "': " + _e.what()); }; - return Parser::read(_r, _var) + return Parser::read(_r, _var) .transform(to_tagged_union) .or_else(embellish_error); @@ -130,8 +131,8 @@ struct Parser> { static void write_wrapped(const W& _w, const T& _val, const P& _parent) noexcept { const auto wrapped = wrap_if_necessary(_val); - Parser>::write(_w, wrapped, - _parent); + Parser, ProcessorsType>::write( + _w, wrapped, _parent); } /// Generates a wrapped version of the original object, which contains the diff --git a/include/rfl/parsing/Parser_tuple.hpp b/include/rfl/parsing/Parser_tuple.hpp index f92d23ce..822d5907 100644 --- a/include/rfl/parsing/Parser_tuple.hpp +++ b/include/rfl/parsing/Parser_tuple.hpp @@ -7,11 +7,11 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, ProcessorsType> : public TupleParser { + /*_all_required=*/false, ProcessorsType, Ts...> { }; } // namespace parsing diff --git a/include/rfl/parsing/Parser_unique_ptr.hpp b/include/rfl/parsing/Parser_unique_ptr.hpp index 9201e0e6..655c9590 100644 --- a/include/rfl/parsing/Parser_unique_ptr.hpp +++ b/include/rfl/parsing/Parser_unique_ptr.hpp @@ -15,9 +15,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> { +struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -31,8 +31,8 @@ struct Parser> { const auto to_ptr = [](auto&& _t) { return std::make_unique(std::move(_t)); }; - return Parser>::read(_r, _var).transform( - to_ptr); + return Parser, ProcessorsType>::read(_r, _var) + .transform(to_ptr); } template @@ -42,14 +42,15 @@ struct Parser> { ParentType::add_null(_w, _parent); return; } - Parser>::write(_w, *_s, _parent); + Parser, ProcessorsType>::write(_w, *_s, + _parent); } static schema::Type to_schema( std::map* _definitions) { using U = std::remove_cvref_t; - return schema::Type{schema::Type::Optional{ - Ref::make(Parser::to_schema(_definitions))}}; + return schema::Type{schema::Type::Optional{Ref::make( + Parser::to_schema(_definitions))}}; } }; diff --git a/include/rfl/parsing/Parser_variant.hpp b/include/rfl/parsing/Parser_variant.hpp index 4caeddd8..fd638919 100644 --- a/include/rfl/parsing/Parser_variant.hpp +++ b/include/rfl/parsing/Parser_variant.hpp @@ -17,9 +17,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -class Parser> { +class Parser, ProcessorsType> { public: using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -27,7 +27,8 @@ class Parser> { static Result> read( const R& _r, const InputVarType& _var) noexcept { if constexpr (internal::all_fields>()) { - return FieldVariantParser::read(_r, _var); + return FieldVariantParser::read( + _r, _var); } else { std::optional> result; std::vector errors; @@ -49,11 +50,12 @@ class Parser> { static void write(const W& _w, const std::variant& _variant, const P& _parent) noexcept { if constexpr (internal::all_fields>()) { - FieldVariantParser::write(_w, _variant, _parent); + FieldVariantParser::write( + _w, _variant, _parent); } else { const auto handle = [&](const auto& _v) { using Type = std::remove_cvref_t; - Parser::write(_w, _v, _parent); + Parser::write(_w, _v, _parent); }; return std::visit(handle, _variant); } @@ -64,7 +66,8 @@ class Parser> { std::map* _definitions, std::vector _types = {}) { if constexpr (internal::all_fields>()) { - return FieldVariantParser::to_schema(_definitions); + return FieldVariantParser::to_schema( + _definitions); } else { using Type = schema::Type; constexpr size_t size = sizeof...(FieldTypes); @@ -73,7 +76,8 @@ class Parser> { } else { using U = std::remove_cvref_t< std::variant_alternative_t<_i, std::variant>>; - _types.push_back(Parser::to_schema(_definitions)); + _types.push_back( + Parser::to_schema(_definitions)); return to_schema<_i + 1>(_definitions, std::move(_types)); } } @@ -88,7 +92,7 @@ class Parser> { if constexpr (_i < size) { using AltType = std::remove_cvref_t< std::variant_alternative_t<_i, std::variant>>; - auto res = Parser::read(_r, _var); + auto res = Parser::read(_r, _var); if (res) { *_result = std::move(*res); return; diff --git a/include/rfl/parsing/Parser_vector_like.hpp b/include/rfl/parsing/Parser_vector_like.hpp index 88eafe94..38ce21ab 100644 --- a/include/rfl/parsing/Parser_vector_like.hpp +++ b/include/rfl/parsing/Parser_vector_like.hpp @@ -21,73 +21,76 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter> -struct Parser> : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; -template +template requires AreReaderAndWriter> -struct Parser> - : public VectorParser> { +struct Parser, ProcessorsType> + : public VectorParser, ProcessorsType> { }; } // namespace parsing diff --git a/include/rfl/parsing/Parser_wstring.hpp b/include/rfl/parsing/Parser_wstring.hpp index 9f3cc457..b524fc9f 100644 --- a/include/rfl/parsing/Parser_wstring.hpp +++ b/include/rfl/parsing/Parser_wstring.hpp @@ -13,9 +13,9 @@ namespace rfl { namespace parsing { -template +template requires AreReaderAndWriter -struct Parser { +struct Parser { public: using InputVarType = typename R::InputVarType; using OutputVarType = typename W::OutputVarType; @@ -28,7 +28,7 @@ struct Parser { return std::wstring(); } - auto inStr = Parser::read(_r, _var); + auto inStr = Parser::read(_r, _var); if (auto err = inStr.error(); err.has_value()) { return Result(err.value()); } @@ -71,7 +71,7 @@ struct Parser { static schema::Type to_schema( std::map* _definitions) { - return Parser::to_schema(_definitions); + return Parser::to_schema(_definitions); } }; diff --git a/include/rfl/parsing/StructReader.hpp b/include/rfl/parsing/StructReader.hpp index 6b9bcc52..e32f2c68 100644 --- a/include/rfl/parsing/StructReader.hpp +++ b/include/rfl/parsing/StructReader.hpp @@ -12,7 +12,7 @@ namespace rfl::parsing { -template +template requires AreReaderAndWriter struct StructReader { public: @@ -25,9 +25,10 @@ struct StructReader { static Result read(const R& _r, const InputVarType& _var) { alignas(StructType) unsigned char buf[sizeof(StructType)]; auto ptr = reinterpret_cast(buf); - const auto view = to_view(*ptr); + auto view = ProcessorsType::template process(to_view(*ptr)); using ViewType = std::remove_cvref_t; - const auto err = Parser::read_view(_r, _var, view); + const auto err = + Parser::read_view(_r, _var, &view); if (err) { return *err; } diff --git a/include/rfl/parsing/TupleParser.hpp b/include/rfl/parsing/TupleParser.hpp index e3bf0c7d..77ac93e0 100644 --- a/include/rfl/parsing/TupleParser.hpp +++ b/include/rfl/parsing/TupleParser.hpp @@ -14,7 +14,7 @@ namespace rfl { namespace parsing { template + class ProcessorsType, class... Ts> requires AreReaderAndWriter> struct TupleParser { public: @@ -68,7 +68,8 @@ struct TupleParser { } else { using U = std::remove_cvref_t>>; - _types.push_back(Parser::to_schema(_definitions)); + _types.push_back( + Parser::to_schema(_definitions)); return to_schema<_i + 1>(_definitions, std::move(_types)); } } @@ -111,7 +112,7 @@ struct TupleParser { } } - return Parser::read(_r, _vec[_i]); + return Parser::read(_r, _vec[_i]); } template @@ -120,7 +121,8 @@ struct TupleParser { if constexpr (_i < sizeof...(Ts)) { using NewFieldType = std::remove_cvref_t< typename std::tuple_element<_i, std::tuple>::type>; - Parser::write(_w, std::get<_i>(_tup), _parent); + Parser::write(_w, std::get<_i>(_tup), + _parent); to_array<_i + 1>(_w, _tup, _parent); } } diff --git a/include/rfl/parsing/VectorParser.hpp b/include/rfl/parsing/VectorParser.hpp index 06aec32e..dc3632b0 100644 --- a/include/rfl/parsing/VectorParser.hpp +++ b/include/rfl/parsing/VectorParser.hpp @@ -25,7 +25,7 @@ namespace parsing { /// serialized format (std::vector, std::set, std::deque, ...), /// but also includes map-like types, when the key is not of type /// std::string. -template +template requires AreReaderAndWriter struct VectorParser { public: @@ -41,7 +41,7 @@ struct VectorParser { static Result read(const R& _r, const InputVarType& _var) noexcept { if constexpr (treat_as_map()) { - return MapParser::read(_r, _var); + return MapParser::read(_r, _var); } else { const auto to_res = [&](auto&& _v) { return to_result(_r, std::move(_v)); @@ -54,13 +54,14 @@ struct VectorParser { static void write(const W& _w, const VecType& _vec, const P& _parent) noexcept { if constexpr (treat_as_map()) { - MapParser::write(_w, _vec, _parent); + MapParser::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>::write(_w, v, new_parent); + Parser, ProcessorsType>::write(_w, v, + new_parent); } _w.end_array(&arr); } @@ -71,18 +72,21 @@ struct VectorParser { std::map* _definitions) { using Type = schema::Type; return Type{Type::TypedArray{ - .type_ = Ref::make(Parser::to_schema(_definitions))}}; + .type_ = Ref::make( + Parser::to_schema(_definitions))}}; } private: static auto get_elem(const R& _r, auto& _v) { - return Parser>::read(_r, _v).value(); + return Parser, ProcessorsType>::read(_r, _v) + .value(); }; static auto get_pair(const R& _r, auto& _v) { using K = std::remove_cvref_t; using V = std::remove_cvref_t; - return Parser>>::read(_r, _v) + return Parser>, + ProcessorsType>::read(_r, _v) .value(); } diff --git a/include/rfl/parsing/ViewReader.hpp b/include/rfl/parsing/ViewReader.hpp index 04f20b69..be37362b 100644 --- a/include/rfl/parsing/ViewReader.hpp +++ b/include/rfl/parsing/ViewReader.hpp @@ -11,16 +11,15 @@ namespace rfl::parsing { -template +template class ViewReader { private: using InputVarType = typename R::InputVarType; static constexpr size_t size_ = ViewType::size(); public: - ViewReader(const R* _r, const ViewType* _view, - std::array* _found, std::array* _set, - std::vector* _errors) + ViewReader(const R* _r, ViewType* _view, std::array* _found, + std::array* _set, std::vector* _errors) : r_(_r), view_(_view), found_(_found), set_(_set), errors_(_errors) {} ~ViewReader() = default; @@ -30,12 +29,19 @@ class ViewReader { if constexpr (_i < size_) { constexpr auto current_name = std::tuple_element_t<_i, typename ViewType::Fields>::name(); - using CurrentType = std::remove_cvref_t::Type>>; + using OriginalType = + typename std::tuple_element_t<_i, typename ViewType::Fields>::Type; + using CurrentType = + std::remove_cvref_t>; if (!std::get<_i>(*found_) && _name == current_name) { - auto res = Parser::read(*r_, _var); + auto res = Parser::read(*r_, _var); if (res) { - move_to(rfl::get<_i>(*view_), &(*res)); + if constexpr (std::is_pointer_v || + internal::is_array_v) { + move_to(rfl::get<_i>(*view_), &(*res)); + } else { + rfl::get<_i>(*view_) = *res; + } std::get<_i>(*set_) = true; } else { errors_->push_back(Error("Failed to parse field '" + @@ -73,7 +79,7 @@ class ViewReader { const R* r_; /// The underlying view. - const ViewType* view_; + ViewType* view_; /// Indicates that a certain field has been found. std::array* found_; diff --git a/include/rfl/parsing/is_view_reader.hpp b/include/rfl/parsing/is_view_reader.hpp index e3290073..42cf30ef 100644 --- a/include/rfl/parsing/is_view_reader.hpp +++ b/include/rfl/parsing/is_view_reader.hpp @@ -11,9 +11,9 @@ class is_view_reader; template class is_view_reader : public std::false_type {}; -template -class is_view_reader> : public std::true_type { -}; +template +class is_view_reader> + : public std::true_type {}; template constexpr bool is_view_reader_v = diff --git a/include/rfl/parsing/schema/make.hpp b/include/rfl/parsing/schema/make.hpp index a6a6886e..94433858 100644 --- a/include/rfl/parsing/schema/make.hpp +++ b/include/rfl/parsing/schema/make.hpp @@ -9,10 +9,10 @@ namespace rfl::parsing::schema { -template +template Definition make() { std::map definitions; - auto root = Parser::to_schema(&definitions); + auto root = Parser::to_schema(&definitions); return Definition{.root_ = root, .definitions_ = definitions}; } diff --git a/include/rfl/to_named_tuple.hpp b/include/rfl/to_named_tuple.hpp index c7d2e017..441dd549 100644 --- a/include/rfl/to_named_tuple.hpp +++ b/include/rfl/to_named_tuple.hpp @@ -19,8 +19,8 @@ namespace rfl { /// Generates the named tuple that is equivalent to the struct _t. /// If _t already is a named tuple, then _t will be returned. /// All fields of the struct must be an rfl::Field. -template -auto to_named_tuple(T&& _t) { +auto to_named_tuple(auto&& _t) { + using T = std::remove_cvref_t; if constexpr (internal::is_named_tuple_v>) { return _t; } else if constexpr (internal::is_field_v>) { @@ -37,8 +37,9 @@ auto to_named_tuple(T&& _t) { /// Generates the named tuple that is equivalent to the struct _t. /// If _t already is a named tuple, then _t will be returned. /// All fields of the struct must be an rfl::Field. -template -auto to_named_tuple(const T& _t) { +template +auto to_named_tuple(const auto& _t) { + using T = std::remove_cvref_t; if constexpr (internal::is_named_tuple_v>) { return _t; } else if constexpr (internal::is_field_v>) { diff --git a/include/rfl/toml.hpp b/include/rfl/toml.hpp index 15b1451e..d24b3538 100644 --- a/include/rfl/toml.hpp +++ b/include/rfl/toml.hpp @@ -1,6 +1,7 @@ #ifndef RFL_TOML_HPP_ #define RFL_TOML_HPP_ +#include "../rfl.hpp" #include "toml/Parser.hpp" #include "toml/Reader.hpp" #include "toml/Writer.hpp" diff --git a/include/rfl/toml/Parser.hpp b/include/rfl/toml/Parser.hpp index d550fe5c..95a3fc5f 100644 --- a/include/rfl/toml/Parser.hpp +++ b/include/rfl/toml/Parser.hpp @@ -7,8 +7,8 @@ namespace rfl::toml { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; } // namespace rfl::toml diff --git a/include/rfl/toml/load.hpp b/include/rfl/toml/load.hpp index f620c87b..1f209218 100644 --- a/include/rfl/toml/load.hpp +++ b/include/rfl/toml/load.hpp @@ -7,9 +7,11 @@ namespace rfl::toml { -template +template Result load(const std::string& _fname) { - const auto read_string = [](const auto& _str) { return read(_str); }; + const auto read_string = [](const auto& _str) { + return read(_str); + }; return rfl::io::load_string(_fname).and_then(read_string); } diff --git a/include/rfl/toml/read.hpp b/include/rfl/toml/read.hpp index d92617b4..14706092 100644 --- a/include/rfl/toml/read.hpp +++ b/include/rfl/toml/read.hpp @@ -5,6 +5,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/wrap_in_rfl_array_t.hpp" #include "Parser.hpp" #include "Reader.hpp" @@ -14,25 +15,25 @@ namespace rfl::toml { using InputVarType = typename Reader::InputVarType; /// Parses an object from a TOML var. -template +template auto read(InputVarType _var) { const auto r = Reader(); - return Parser::read(r, _var); + return Parser>::read(r, _var); } /// Parses an object from TOML using reflection. -template +template Result> read(const std::string& _toml_str) { auto table = ::toml::parse(_toml_str); - return read(&table); + return read(&table); } /// Parses an object from a stringstream. -template +template auto read(std::istream& _stream) { const auto toml_str = std::string(std::istreambuf_iterator(_stream), std::istreambuf_iterator()); - return read(toml_str); + return read(toml_str); } } // namespace rfl::toml diff --git a/include/rfl/toml/save.hpp b/include/rfl/toml/save.hpp index 0b77d9aa..d15bd7b7 100644 --- a/include/rfl/toml/save.hpp +++ b/include/rfl/toml/save.hpp @@ -12,10 +12,10 @@ namespace rfl { namespace toml { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream); + return write(_obj, _stream); }; return rfl::io::save_string(_fname, _obj, write_func); } diff --git a/include/rfl/toml/write.hpp b/include/rfl/toml/write.hpp index 3a684f78..b1426127 100644 --- a/include/rfl/toml/write.hpp +++ b/include/rfl/toml/write.hpp @@ -7,30 +7,33 @@ #include #include +#include "../Processors.hpp" #include "../parsing/Parent.hpp" #include "Parser.hpp" namespace rfl::toml { /// Writes a TOML into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream) { +template +std::ostream& write(const auto& _obj, std::ostream& _stream) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; ::toml::table root; auto w = Writer(&root); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); _stream << root; return _stream; } /// Returns a TOML string. -template -std::string write(const T& _obj) { +template +std::string write(const auto& _obj) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; std::stringstream sstream; ::toml::table root; auto w = Writer(&root); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); sstream << root; return sstream.str(); } diff --git a/include/rfl/xml.hpp b/include/rfl/xml.hpp index dd3a8947..ecb20c82 100644 --- a/include/rfl/xml.hpp +++ b/include/rfl/xml.hpp @@ -1,6 +1,7 @@ #ifndef RFL_XML_HPP_ #define RFL_XML_HPP_ +#include "../rfl.hpp" #include "xml/Parser.hpp" #include "xml/Reader.hpp" #include "xml/Writer.hpp" diff --git a/include/rfl/xml/Parser.hpp b/include/rfl/xml/Parser.hpp index ec9b38fa..f74786c9 100644 --- a/include/rfl/xml/Parser.hpp +++ b/include/rfl/xml/Parser.hpp @@ -15,12 +15,14 @@ namespace parsing { /// XML is very special. It doesn't have proper support for arrays, which means /// that we just need to ignore empty containers. Therefore, we need to a /// template specialization for the NamedTuple parser to accommodate for it. -template +template requires AreReaderAndWriter> -struct Parser> +struct Parser, + ProcessorsType> : public NamedTupleParser { + /*_all_required=*/false, ProcessorsType, + FieldTypes...> { }; } // namespace parsing @@ -29,8 +31,8 @@ struct Parser> namespace rfl { namespace xml { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; } } // namespace rfl diff --git a/include/rfl/xml/load.hpp b/include/rfl/xml/load.hpp index b2b73291..861f8c41 100644 --- a/include/rfl/xml/load.hpp +++ b/include/rfl/xml/load.hpp @@ -8,9 +8,11 @@ namespace rfl { namespace xml { -template +template Result load(const std::string& _fname) { - const auto read_string = [](const auto& _str) { return read(_str); }; + const auto read_string = [](const auto& _str) { + return read(_str); + }; return rfl::io::load_string(_fname).and_then(read_string); } diff --git a/include/rfl/xml/read.hpp b/include/rfl/xml/read.hpp index a13d45c2..e20d4c5b 100644 --- a/include/rfl/xml/read.hpp +++ b/include/rfl/xml/read.hpp @@ -5,6 +5,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/get_type_name.hpp" #include "../internal/remove_namespaces.hpp" #include "Parser.hpp" @@ -16,14 +17,14 @@ namespace xml { using InputVarType = typename Reader::InputVarType; /// Parses an object from a XML var. -template +template auto read(const InputVarType& _var) { const auto r = Reader(); - return Parser::read(r, _var); + return Parser>::read(r, _var); } /// Parses an object from XML using reflection. -template +template Result read(const std::string& _xml_str) { pugi::xml_document doc; const auto result = doc.load_string(_xml_str.c_str()); @@ -32,15 +33,15 @@ Result read(const std::string& _xml_str) { std::string(result.description())); } const auto var = InputVarType(doc.first_child()); - return read(var); + return read(var); } /// Parses an object from a stringstream. -template +template auto read(std::istream& _stream) { const auto xml_str = std::string(std::istreambuf_iterator(_stream), std::istreambuf_iterator()); - return read(xml_str); + return read(xml_str); } } // namespace xml diff --git a/include/rfl/xml/save.hpp b/include/rfl/xml/save.hpp index b392023a..48c3e5c9 100644 --- a/include/rfl/xml/save.hpp +++ b/include/rfl/xml/save.hpp @@ -13,10 +13,11 @@ namespace rfl { namespace xml { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write<_root>(_obj, _stream); + return write<_root, Ps...>(_obj, _stream); }; return rfl::io::save_string(_fname, _obj, write_func); } diff --git a/include/rfl/xml/write.hpp b/include/rfl/xml/write.hpp index 7e39447d..023c7577 100644 --- a/include/rfl/xml/write.hpp +++ b/include/rfl/xml/write.hpp @@ -7,6 +7,7 @@ #include #include +#include "../Processors.hpp" #include "../internal/StringLiteral.hpp" #include "../internal/get_type_name.hpp" #include "../internal/remove_namespaces.hpp" @@ -27,9 +28,11 @@ consteval auto get_root_name() { } /// Writes a XML into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream, +template +std::ostream& write(const auto& _obj, std::ostream& _stream, const std::string& _indent = " ") { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; constexpr auto root_name = get_root_name<_root, T>(); @@ -49,7 +52,7 @@ std::ostream& write(const T& _obj, std::ostream& _stream, auto w = Writer(doc, root_name.str()); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); doc->save(_stream, _indent.c_str()); @@ -57,10 +60,11 @@ std::ostream& write(const T& _obj, std::ostream& _stream, } /// Returns a XML string. -template -std::string write(const T& _obj, const std::string& _indent = " ") { +template +std::string write(const auto& _obj, const std::string& _indent = " ") { std::stringstream stream; - write<_root, T>(_obj, stream); + write<_root, Ps...>(_obj, stream); return stream.str(); } diff --git a/include/rfl/yaml.hpp b/include/rfl/yaml.hpp index 5b424013..11cb13eb 100644 --- a/include/rfl/yaml.hpp +++ b/include/rfl/yaml.hpp @@ -1,6 +1,7 @@ #ifndef RFL_YAML_HPP_ #define RFL_YAML_HPP_ +#include "../rfl.hpp" #include "yaml/Parser.hpp" #include "yaml/Reader.hpp" #include "yaml/Writer.hpp" diff --git a/include/rfl/yaml/Parser.hpp b/include/rfl/yaml/Parser.hpp index bb69f00c..78ef3354 100644 --- a/include/rfl/yaml/Parser.hpp +++ b/include/rfl/yaml/Parser.hpp @@ -8,8 +8,8 @@ namespace rfl { namespace yaml { -template -using Parser = parsing::Parser; +template +using Parser = parsing::Parser; } } // namespace rfl diff --git a/include/rfl/yaml/load.hpp b/include/rfl/yaml/load.hpp index 255eee73..7f3fa92d 100644 --- a/include/rfl/yaml/load.hpp +++ b/include/rfl/yaml/load.hpp @@ -8,9 +8,11 @@ namespace rfl { namespace yaml { -template +template Result load(const std::string& _fname) { - const auto read_string = [](const auto& _str) { return read(_str); }; + const auto read_string = [](const auto& _str) { + return read(_str); + }; return rfl::io::load_string(_fname).and_then(read_string); } diff --git a/include/rfl/yaml/read.hpp b/include/rfl/yaml/read.hpp index e3b38ec8..0d00eb6d 100644 --- a/include/rfl/yaml/read.hpp +++ b/include/rfl/yaml/read.hpp @@ -6,39 +6,39 @@ #include #include +#include "../Processors.hpp" #include "../internal/wrap_in_rfl_array_t.hpp" #include "Parser.hpp" #include "Reader.hpp" - namespace rfl { namespace yaml { using InputVarType = typename Reader::InputVarType; /// Parses an object from a YAML var. -template +template auto read(const InputVarType& _var) { const auto r = Reader(); - return Parser::read(r, _var); + return Parser>::read(r, _var); } /// Parses an object from YAML using reflection. -template +template Result> read(const std::string& _yaml_str) { try { const auto var = InputVarType(YAML::Load(_yaml_str)); - return read(var); + return read(var); } catch (std::exception& e) { return Error(e.what()); } } /// Parses an object from a stringstream. -template +template auto read(std::istream& _stream) { const auto yaml_str = std::string(std::istreambuf_iterator(_stream), std::istreambuf_iterator()); - return read(yaml_str); + return read(yaml_str); } } // namespace yaml diff --git a/include/rfl/yaml/save.hpp b/include/rfl/yaml/save.hpp index 2822be24..6333c99e 100644 --- a/include/rfl/yaml/save.hpp +++ b/include/rfl/yaml/save.hpp @@ -5,6 +5,7 @@ #include #include +#include "../Processors.hpp" #include "../Result.hpp" #include "../io/save_string.hpp" #include "write.hpp" @@ -12,10 +13,10 @@ namespace rfl { namespace yaml { -template -Result save(const std::string& _fname, const T& _obj) { +template +Result save(const std::string& _fname, const auto& _obj) { const auto write_func = [](const auto& _obj, auto& _stream) -> auto& { - return write(_obj, _stream); + return write(_obj, _stream); }; return rfl::io::save_string(_fname, _obj, write_func); } diff --git a/include/rfl/yaml/write.hpp b/include/rfl/yaml/write.hpp index e855e5ea..f6613ce1 100644 --- a/include/rfl/yaml/write.hpp +++ b/include/rfl/yaml/write.hpp @@ -8,34 +8,37 @@ #include #include +#include "../Processors.hpp" #include "../parsing/Parent.hpp" #include "Parser.hpp" namespace rfl { namespace yaml { -/// Writes a XML into an ostream. -template -std::ostream& write(const T& _obj, std::ostream& _stream) { +/// Writes a YAML into an ostream. +template +std::ostream& write(const auto& _obj, std::ostream& _stream) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; const auto out = Ref::make(); auto w = Writer(out); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); _stream << out->c_str(); return _stream; } -/// Returns a XML string. -template -std::string write(const T& _obj) { +/// Returns a YAML string. +template +std::string write(const auto& _obj) { + using T = std::remove_cvref_t; using ParentType = parsing::Parent; const auto out = Ref::make(); auto w = Writer(out); - Parser::write(w, _obj, typename ParentType::Root{}); + Parser>::write(w, _obj, typename ParentType::Root{}); return out->c_str(); } } // namespace yaml } // namespace rfl -#endif // XML_PARSER_HPP_ +#endif diff --git a/tests/bson/test_add_struct_name.cpp b/tests/bson/test_add_struct_name.cpp new file mode 100644 index 00000000..a965527e --- /dev/null +++ b/tests/bson/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(bson, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/bson/test_combined_processors.cpp b/tests/bson/test_combined_processors.cpp new file mode 100644 index 00000000..80f6b0e7 --- /dev/null +++ b/tests/bson/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(bson, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read(homer); +} +} // namespace test_combined_processors diff --git a/tests/bson/test_custom_class3.cpp b/tests/bson/test_custom_class3.cpp index 75cd152e..803e2451 100644 --- a/tests/bson/test_custom_class3.cpp +++ b/tests/bson/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/bson/test_custom_class4.cpp b/tests/bson/test_custom_class4.cpp index 8a0edea4..cfda6c76 100644 --- a/tests/bson/test_custom_class4.cpp +++ b/tests/bson/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/bson/test_snake_case_to_camel_case.cpp b/tests/bson/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..fa14e563 --- /dev/null +++ b/tests/bson/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(bson, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/bson/test_snake_case_to_pascal_case.cpp b/tests/bson/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..d5fa0138 --- /dev/null +++ b/tests/bson/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(bson, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/bson/write_and_read.hpp b/tests/bson/write_and_read.hpp index 15096a38..fc9814a0 100644 --- a/tests/bson/write_and_read.hpp +++ b/tests/bson/write_and_read.hpp @@ -7,13 +7,14 @@ #include #include -template -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::bson::write(_struct); - const auto res = rfl::bson::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::bson::write(_struct); + const auto res = rfl::bson::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::bson::write(res.value()); + const auto serialized2 = rfl::bson::write(res.value()); EXPECT_EQ(serialized1, serialized2); } diff --git a/tests/cbor/test_add_struct_name.cpp b/tests/cbor/test_add_struct_name.cpp new file mode 100644 index 00000000..62e2ca3a --- /dev/null +++ b/tests/cbor/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(cbor, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/cbor/test_combined_processors.cpp b/tests/cbor/test_combined_processors.cpp new file mode 100644 index 00000000..8ceb449b --- /dev/null +++ b/tests/cbor/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(cbor, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read(homer); +} +} // namespace test_combined_processors diff --git a/tests/cbor/test_custom_class3.cpp b/tests/cbor/test_custom_class3.cpp index 991df513..ffa1d3e5 100644 --- a/tests/cbor/test_custom_class3.cpp +++ b/tests/cbor/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/cbor/test_custom_class4.cpp b/tests/cbor/test_custom_class4.cpp index 45b6d914..44b8f17e 100644 --- a/tests/cbor/test_custom_class4.cpp +++ b/tests/cbor/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/cbor/test_snake_case_to_camel_case.cpp b/tests/cbor/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..81ca37e1 --- /dev/null +++ b/tests/cbor/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(cbor, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/cbor/test_snake_case_to_pascal_case.cpp b/tests/cbor/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..a78d1048 --- /dev/null +++ b/tests/cbor/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(cbor, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/cbor/write_and_read.hpp b/tests/cbor/write_and_read.hpp index f0078856..8bbf4d0f 100644 --- a/tests/cbor/write_and_read.hpp +++ b/tests/cbor/write_and_read.hpp @@ -7,14 +7,14 @@ #include #include -template -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::cbor::write(_struct); - const auto res = rfl::cbor::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::cbor::write(_struct); + const auto res = rfl::cbor::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::cbor::write(res.value()); + const auto serialized2 = rfl::cbor::write(res.value()); EXPECT_EQ(serialized1, serialized2); } - #endif diff --git a/tests/flexbuffers/test_add_struct_name.cpp b/tests/flexbuffers/test_add_struct_name.cpp new file mode 100644 index 00000000..45c343d8 --- /dev/null +++ b/tests/flexbuffers/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(flexbuf, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/flexbuffers/test_combined_processors.cpp b/tests/flexbuffers/test_combined_processors.cpp new file mode 100644 index 00000000..5c76e0e9 --- /dev/null +++ b/tests/flexbuffers/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(flexbuf, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read(homer); +} +} // namespace test_combined_processors diff --git a/tests/flexbuffers/test_custom_class3.cpp b/tests/flexbuffers/test_custom_class3.cpp index edd41c42..1ad99383 100644 --- a/tests/flexbuffers/test_custom_class3.cpp +++ b/tests/flexbuffers/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/flexbuffers/test_custom_class4.cpp b/tests/flexbuffers/test_custom_class4.cpp index 1ba35f4a..6cf94a19 100644 --- a/tests/flexbuffers/test_custom_class4.cpp +++ b/tests/flexbuffers/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/flexbuffers/test_snake_case_to_camel_case.cpp b/tests/flexbuffers/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..9b825991 --- /dev/null +++ b/tests/flexbuffers/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(flexbuf, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/flexbuffers/test_snake_case_to_pascal_case.cpp b/tests/flexbuffers/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..f574d01c --- /dev/null +++ b/tests/flexbuffers/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(flexbuf, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/flexbuffers/write_and_read.hpp b/tests/flexbuffers/write_and_read.hpp index a7f6d9ac..daab3062 100644 --- a/tests/flexbuffers/write_and_read.hpp +++ b/tests/flexbuffers/write_and_read.hpp @@ -7,13 +7,14 @@ #include #include -template -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::flexbuf::write(_struct); - const auto res = rfl::flexbuf::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::flexbuf::write(_struct); + const auto res = rfl::flexbuf::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::flexbuf::write(res.value()); + const auto serialized2 = rfl::flexbuf::write(res.value()); EXPECT_EQ(serialized1, serialized2); } diff --git a/tests/json/test_add_struct_name.cpp b/tests/json/test_add_struct_name.cpp new file mode 100644 index 00000000..74db9e22 --- /dev/null +++ b/tests/json/test_add_struct_name.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(json, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>( + homer, + R"({"type":"Person","firstName":"Homer","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":45,"email":"homer@simpson.com","children":[{"type":"Person","firstName":"Bart","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":10,"email":"bart@simpson.com","children":[]},{"type":"Person","firstName":"Lisa","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":8,"email":"lisa@simpson.com","children":[]},{"type":"Person","firstName":"Maggie","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":0,"email":"maggie@simpson.com","children":[]}]})"); +} +} // namespace test_add_struct_name diff --git a/tests/json/test_combined_processors.cpp b/tests/json/test_combined_processors.cpp new file mode 100644 index 00000000..c6de9d1a --- /dev/null +++ b/tests/json/test_combined_processors.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(json, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read( + homer, + R"({"type":"Person","firstName":"Homer","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":45,"email":"homer@simpson.com","children":[{"type":"Person","firstName":"Bart","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":10,"email":"bart@simpson.com","children":[]},{"type":"Person","firstName":"Lisa","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":8,"email":"lisa@simpson.com","children":[]},{"type":"Person","firstName":"Maggie","lastName":"Simpson","town":"Springfield","birthday":"1987-04-19","age":0,"email":"maggie@simpson.com","children":[]}]})"); +} +} // namespace test_combined_processors diff --git a/tests/json/test_custom_class3.cpp b/tests/json/test_custom_class3.cpp index c476b555..efe51d7f 100644 --- a/tests/json/test_custom_class3.cpp +++ b/tests/json/test_custom_class3.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/json/test_custom_class4.cpp b/tests/json/test_custom_class4.cpp index 4e83124f..7faae5e3 100644 --- a/tests/json/test_custom_class4.cpp +++ b/tests/json/test_custom_class4.cpp @@ -46,9 +46,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/json/test_json_schema2.cpp b/tests/json/test_json_schema2.cpp new file mode 100644 index 00000000..7b55973f --- /dev/null +++ b/tests/json/test_json_schema2.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_json_schema2 { + +enum class Color { red, green, blue }; + +struct Rectangle { + double width; + double height; +}; + +struct Square { + double width; +}; + +struct Circle { + double radius; +}; + +using Age = rfl::Validator, rfl::Maximum<130>>; + +using FieldVariant = + rfl::Variant, + rfl::Field<"square", Square>, rfl::Field<"circle", Circle>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + rfl::Description<"Must be a proper email in the form xxx@xxx.xxx.", + rfl::Email> + email; + std::string town = "Springfield"; + Color color; + Age age; + float salary; + rfl::Description< + "The person's children. Pass an empty array for no children.", + std::vector> + children; + std::variant, int> variant; + std::tuple, int> tuple; + rfl::Rename<"taggedUnion", + rfl::TaggedUnion<"shape", Rectangle, Square, Circle>> + tagged_union; + rfl::Rename<"fieldVariant", FieldVariant> field_variant; +}; + +TEST(json, test_json_schema2) { + using DescribedType = rfl::Description< + "JSON schema that describes the required " + "attributes for the person class.", + Person>; + + const auto json_schema = + rfl::json::to_schema(); + + const std::string expected = + R"({"$schema":"https://json-schema.org/draft/2020-12/schema","$ref":"#/definitions/test_json_schema2__Person","description":"JSON schema that describes the required attributes for the person class.","definitions":{"test_json_schema2__Circle":{"type":"object","properties":{"radius":{"type":"number"}},"required":["radius"]},"test_json_schema2__Circle__tagged":{"type":"object","properties":{"radius":{"type":"number"},"shape":{"type":"string","enum":["Circle"]}},"required":["radius","shape"]},"test_json_schema2__Person":{"type":"object","properties":{"age":{"allOf":[{"minimum":0,"type":"integer"},{"maximum":130,"type":"integer"}]},"children":{"type":"array","description":"The person's children. Pass an empty array for no children.","items":{"$ref":"#/definitions/test_json_schema2__Person"}},"color":{"type":"string","enum":["red","green","blue"]},"email":{"type":"string","description":"Must be a proper email in the form xxx@xxx.xxx.","pattern":"^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$"},"fieldVariant":{"anyOf":[{"type":"object","properties":{"rectangle":{"$ref":"#/definitions/test_json_schema2__Rectangle"}},"required":["rectangle"]},{"type":"object","properties":{"square":{"$ref":"#/definitions/test_json_schema2__Square"}},"required":["square"]},{"type":"object","properties":{"circle":{"$ref":"#/definitions/test_json_schema2__Circle"}},"required":["circle"]}]},"firstName":{"type":"string"},"lastName":{"type":"string"},"salary":{"type":"number"},"taggedUnion":{"anyOf":[{"$ref":"#/definitions/test_json_schema2__Rectangle__tagged"},{"$ref":"#/definitions/test_json_schema2__Square__tagged"},{"$ref":"#/definitions/test_json_schema2__Circle__tagged"}]},"town":{"type":"string"},"tuple":{"type":"array","prefixItems":[{"type":"string","enum":["red","green","blue"]},{"type":"array","items":{"$ref":"#/definitions/test_json_schema2__Person"}},{"type":"integer"}],"items":false},"variant":{"anyOf":[{"type":"string","enum":["red","green","blue"]},{"type":"array","items":{"$ref":"#/definitions/test_json_schema2__Person"}},{"type":"integer"}]}},"required":["age","children","color","email","fieldVariant","firstName","lastName","salary","taggedUnion","town","tuple","variant"]},"test_json_schema2__Rectangle":{"type":"object","properties":{"height":{"type":"number"},"width":{"type":"number"}},"required":["height","width"]},"test_json_schema2__Rectangle__tagged":{"type":"object","properties":{"height":{"type":"number"},"shape":{"type":"string","enum":["Rectangle"]},"width":{"type":"number"}},"required":["height","shape","width"]},"test_json_schema2__Square":{"type":"object","properties":{"width":{"type":"number"}},"required":["width"]},"test_json_schema2__Square__tagged":{"type":"object","properties":{"shape":{"type":"string","enum":["Square"]},"width":{"type":"number"}},"required":["shape","width"]}}})"; + + EXPECT_EQ(json_schema, expected); +} +} // namespace test_json_schema2 diff --git a/tests/json/test_snake_case_to_camel_case.cpp b/tests/json/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..85592e00 --- /dev/null +++ b/tests/json/test_snake_case_to_camel_case.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(json, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read( + homer, + R"({"firstName":"Homer","lastName":"Simpson","birthday":"1987-04-19","children":[{"firstName":"Bart","lastName":"Simpson","birthday":"1987-04-19","children":[]},{"firstName":"Lisa","lastName":"Simpson","birthday":"1987-04-19","children":[]},{"firstName":"Maggie","lastName":"Simpson","birthday":"1987-04-19","children":[]}]})"); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/json/test_snake_case_to_pascal_case.cpp b/tests/json/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..be0a6bce --- /dev/null +++ b/tests/json/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(json, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read( + homer, + R"({"FirstName":"Homer","LastName":"Simpson","Birthday":"1987-04-19","Children":[{"FirstName":"Bart","LastName":"Simpson","Birthday":"1987-04-19","Children":[]},{"FirstName":"Lisa","LastName":"Simpson","Birthday":"1987-04-19","Children":[]},{"FirstName":"Maggie","LastName":"Simpson","Birthday":"1987-04-19","Children":[]}]})"); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/json/write_and_read.hpp b/tests/json/write_and_read.hpp index 5f2c9e79..a015e75b 100644 --- a/tests/json/write_and_read.hpp +++ b/tests/json/write_and_read.hpp @@ -6,20 +6,22 @@ #include #include #include +#include -template -void write_and_read(const T& _struct, const std::string& _expected) { - const auto json_string1 = rfl::json::write(_struct); +template +void write_and_read(const auto& _struct, const std::string& _expected) { + using T = std::remove_cvref_t; + const auto json_string1 = rfl::json::write(_struct); EXPECT_EQ(json_string1, _expected) << "Test failed on write. Expected:" << std::endl << _expected << std::endl << "Got: " << std::endl << json_string1 << std::endl << std::endl; - const auto res = rfl::json::read(json_string1); + const auto res = rfl::json::read(json_string1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto json_string2 = rfl::json::write(res.value()); + const auto json_string2 = rfl::json::write(res.value()); EXPECT_EQ(json_string2, _expected) << "Test failed on read. Expected:" << std::endl << _expected << std::endl diff --git a/tests/msgpack/test_add_struct_name.cpp b/tests/msgpack/test_add_struct_name.cpp new file mode 100644 index 00000000..b9d6f7ad --- /dev/null +++ b/tests/msgpack/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(msgpack, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/msgpack/test_combined_processors.cpp b/tests/msgpack/test_combined_processors.cpp new file mode 100644 index 00000000..e447aa98 --- /dev/null +++ b/tests/msgpack/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(msgpack, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read(homer); +} +} // namespace test_combined_processors diff --git a/tests/msgpack/test_custom_class3.cpp b/tests/msgpack/test_custom_class3.cpp index 7ef3a094..6a1b1c08 100644 --- a/tests/msgpack/test_custom_class3.cpp +++ b/tests/msgpack/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/msgpack/test_custom_class4.cpp b/tests/msgpack/test_custom_class4.cpp index 46906e0f..b1b6388b 100644 --- a/tests/msgpack/test_custom_class4.cpp +++ b/tests/msgpack/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/msgpack/test_snake_case_to_camel_case.cpp b/tests/msgpack/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..6ff299f1 --- /dev/null +++ b/tests/msgpack/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(msgpack, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/msgpack/test_snake_case_to_pascal_case.cpp b/tests/msgpack/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..04b8327e --- /dev/null +++ b/tests/msgpack/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(msgpack, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/msgpack/write_and_read.hpp b/tests/msgpack/write_and_read.hpp index bd1918d9..eb6d14b6 100644 --- a/tests/msgpack/write_and_read.hpp +++ b/tests/msgpack/write_and_read.hpp @@ -7,13 +7,14 @@ #include #include -template -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::msgpack::write(_struct); - const auto res = rfl::msgpack::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::msgpack::write(_struct); + const auto res = rfl::msgpack::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::msgpack::write(res.value()); + const auto serialized2 = rfl::msgpack::write(res.value()); EXPECT_EQ(serialized1, serialized2); } diff --git a/tests/toml/test_add_struct_name.cpp b/tests/toml/test_add_struct_name.cpp new file mode 100644 index 00000000..4d0ba40c --- /dev/null +++ b/tests/toml/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(toml, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/toml/test_combined_processors.cpp b/tests/toml/test_combined_processors.cpp new file mode 100644 index 00000000..c448f50e --- /dev/null +++ b/tests/toml/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(toml, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read(homer); +} +} // namespace test_combined_processors diff --git a/tests/toml/test_custom_class3.cpp b/tests/toml/test_custom_class3.cpp index c5f70a25..d7ce343f 100644 --- a/tests/toml/test_custom_class3.cpp +++ b/tests/toml/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/toml/test_custom_class4.cpp b/tests/toml/test_custom_class4.cpp index fc3c009e..7da6ec7a 100644 --- a/tests/toml/test_custom_class4.cpp +++ b/tests/toml/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/toml/test_snake_case_to_camel_case.cpp b/tests/toml/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..87cc7afb --- /dev/null +++ b/tests/toml/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(toml, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/toml/test_snake_case_to_pascal_case.cpp b/tests/toml/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..d1e3ca4d --- /dev/null +++ b/tests/toml/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(toml, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/toml/write_and_read.hpp b/tests/toml/write_and_read.hpp index 4c45b5d5..a46c8fe6 100644 --- a/tests/toml/write_and_read.hpp +++ b/tests/toml/write_and_read.hpp @@ -7,13 +7,14 @@ #include #include -template -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::toml::write(_struct); - const auto res = rfl::toml::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::toml::write(_struct); + const auto res = rfl::toml::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::toml::write(res.value()); + const auto serialized2 = rfl::toml::write(res.value()); EXPECT_EQ(serialized1, serialized2); } diff --git a/tests/xml/test_add_struct_name.cpp b/tests/xml/test_add_struct_name.cpp new file mode 100644 index 00000000..61ec9f26 --- /dev/null +++ b/tests/xml/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(xml, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read<"", rfl::AddStructName<"type">>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/xml/test_combined_processors.cpp b/tests/xml/test_combined_processors.cpp new file mode 100644 index 00000000..6e2f30ba --- /dev/null +++ b/tests/xml/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(xml, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read<"", Processors>(homer); +} +} // namespace test_combined_processors diff --git a/tests/xml/test_custom_class3.cpp b/tests/xml/test_custom_class3.cpp index 3cbc23f6..93b33105 100644 --- a/tests/xml/test_custom_class3.cpp +++ b/tests/xml/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/xml/test_custom_class4.cpp b/tests/xml/test_custom_class4.cpp index 55a5c693..f8a08769 100644 --- a/tests/xml/test_custom_class4.cpp +++ b/tests/xml/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/xml/test_snake_case_to_camel_case.cpp b/tests/xml/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..0070858f --- /dev/null +++ b/tests/xml/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(xml, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read<"", rfl::SnakeCaseToCamelCase>(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/xml/test_snake_case_to_pascal_case.cpp b/tests/xml/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..e7520127 --- /dev/null +++ b/tests/xml/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(xml, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read<"", rfl::SnakeCaseToPascalCase>(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/xml/test_xml_content2.cpp b/tests/xml/test_xml_content2.cpp new file mode 100644 index 00000000..365d37e1 --- /dev/null +++ b/tests/xml/test_xml_content2.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_xml_content2 { + +struct Person { + std::string xml_content; + rfl::Attribute town = "Springfield"; + rfl::Attribute> birthday; + rfl::Attribute email; + std::vector child; +}; + +TEST(xml, test_xml_content2) { + const auto bart = Person{.xml_content = "Bart Simpson", + .birthday = "1987-04-19", + .email = "bart@simpson.com"}; + + const auto lisa = Person{.xml_content = "Lisa Simpson", + .birthday = "1987-04-19", + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.xml_content = "Maggie Simpson", + .birthday = "1987-04-19", + .email = "maggie@simpson.com"}; + + const auto homer = Person{.xml_content = "Homer Simpson", + .birthday = "1987-04-19", + .email = "homer@simpson.com", + .child = std::vector({bart, lisa, maggie})}; + + write_and_read<"", rfl::SnakeCaseToCamelCase>(homer); +} +} // namespace test_xml_content2 diff --git a/tests/xml/write_and_read.hpp b/tests/xml/write_and_read.hpp index a3aa4719..a992b5f9 100644 --- a/tests/xml/write_and_read.hpp +++ b/tests/xml/write_and_read.hpp @@ -8,15 +8,14 @@ #include #include -template < - rfl::internal::StringLiteral _root_name = rfl::internal::StringLiteral(""), - class T> -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::xml::write<_root_name>(_struct); - const auto res = rfl::xml::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::xml::write<_root_name, Ps...>(_struct); + const auto res = rfl::xml::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::xml::write<_root_name>(res.value()); + const auto serialized2 = rfl::xml::write<_root_name, Ps...>(res.value()); EXPECT_EQ(serialized1, serialized2); } diff --git a/tests/yaml/test_add_struct_name.cpp b/tests/yaml/test_add_struct_name.cpp new file mode 100644 index 00000000..4f2fdf14 --- /dev/null +++ b/tests/yaml/test_add_struct_name.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_add_struct_name { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + rfl::Rename<"firstName", std::string> first_name; + rfl::Rename<"lastName", std::string> last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(yaml, test_add_struct_name) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read>(homer); +} +} // namespace test_add_struct_name diff --git a/tests/yaml/test_combined_processors.cpp b/tests/yaml/test_combined_processors.cpp new file mode 100644 index 00000000..d046b7fa --- /dev/null +++ b/tests/yaml/test_combined_processors.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_combined_processors { + +using Age = rfl::Validator, rfl::Maximum<130>>; + +struct Person { + std::string first_name; + std::string last_name = "Simpson"; + std::string town = "Springfield"; + rfl::Timestamp<"%Y-%m-%d"> birthday; + Age age; + rfl::Email email; + std::vector children; +}; + +TEST(yaml, test_combined_processors) { + const auto bart = Person{.first_name = "Bart", + .birthday = "1987-04-19", + .age = 10, + .email = "bart@simpson.com"}; + + const auto lisa = Person{.first_name = "Lisa", + .birthday = "1987-04-19", + .age = 8, + .email = "lisa@simpson.com"}; + + const auto maggie = Person{.first_name = "Maggie", + .birthday = "1987-04-19", + .age = 0, + .email = "maggie@simpson.com"}; + + const auto homer = + Person{.first_name = "Homer", + .birthday = "1987-04-19", + .age = 45, + .email = "homer@simpson.com", + .children = std::vector({bart, lisa, maggie})}; + + using Processors = + rfl::Processors>; + + write_and_read(homer); +} +} // namespace test_combined_processors diff --git a/tests/yaml/test_custom_class3.cpp b/tests/yaml/test_custom_class3.cpp index a59d5c22..f741401c 100644 --- a/tests/yaml/test_custom_class3.cpp +++ b/tests/yaml/test_custom_class3.cpp @@ -44,9 +44,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/yaml/test_custom_class4.cpp b/tests/yaml/test_custom_class4.cpp index 43bccf9f..97a468ee 100644 --- a/tests/yaml/test_custom_class4.cpp +++ b/tests/yaml/test_custom_class4.cpp @@ -45,9 +45,11 @@ struct PersonImpl { namespace rfl { namespace parsing { -template -struct Parser - : public CustomParser +struct Parser + : public CustomParser {}; } // namespace parsing diff --git a/tests/yaml/test_snake_case_to_camel_case.cpp b/tests/yaml/test_snake_case_to_camel_case.cpp new file mode 100644 index 00000000..c96c0a56 --- /dev/null +++ b/tests/yaml/test_snake_case_to_camel_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_camel_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(yaml, test_snake_case_to_camel_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_camel_case diff --git a/tests/yaml/test_snake_case_to_pascal_case.cpp b/tests/yaml/test_snake_case_to_pascal_case.cpp new file mode 100644 index 00000000..d5fa0138 --- /dev/null +++ b/tests/yaml/test_snake_case_to_pascal_case.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_snake_case_to_pascal_case { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Timestamp<"%Y-%m-%d"> birthday; + std::vector children; +}; + +TEST(bson, test_snake_case_to_pascal_case) { + const auto bart = Person{ + .first_name = "Bart", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto lisa = Person{ + .first_name = "Lisa", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto maggie = Person{ + .first_name = "Maggie", .last_name = "Simpson", .birthday = "1987-04-19"}; + + const auto homer = + Person{.first_name = "Homer", + .last_name = "Simpson", + .birthday = "1987-04-19", + .children = std::vector({bart, lisa, maggie})}; + + write_and_read(homer); +} +} // namespace test_snake_case_to_pascal_case diff --git a/tests/yaml/write_and_read.hpp b/tests/yaml/write_and_read.hpp index 6e4d5922..87cd642e 100644 --- a/tests/yaml/write_and_read.hpp +++ b/tests/yaml/write_and_read.hpp @@ -7,13 +7,14 @@ #include #include -template -void write_and_read(const T& _struct) { - const auto serialized1 = rfl::yaml::write(_struct); - const auto res = rfl::yaml::read(serialized1); +template +void write_and_read(const auto& _struct) { + using T = std::remove_cvref_t; + const auto serialized1 = rfl::yaml::write(_struct); + const auto res = rfl::yaml::read(serialized1); EXPECT_TRUE(res && true) << "Test failed on read. Error: " << res.error().value().what(); - const auto serialized2 = rfl::yaml::write(res.value()); + const auto serialized2 = rfl::yaml::write(res.value()); EXPECT_EQ(serialized1, serialized2); }