Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion include/rfl/yaml/Reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,22 @@ struct Reader {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>() ||
std::is_same<std::remove_cvref_t<T>, bool>() ||
std::is_floating_point<std::remove_cvref_t<T>>()) {
return _var.node_.as<std::remove_cvref_t<T>>();
auto result = _var.node_.as<std::remove_cvref_t<T>>();
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
// In case of multi-line YAML literal strings, yaml-cpp may parse an
// extra new line which is not there intentionally. This may break
// multiple re-serialization checks, for this reason we trim trailing
// new-lines here.
//
// It would be preferable to do this only if input string was stored
// as a multiline string literal, but unfortunately yaml-cpp doesn't
// seem to expose that information.
auto last_non_new_line = result.find_last_not_of("\r\n");
if (last_non_new_line != std::string::npos) {
result = result.substr(0, last_non_new_line + 1);
}
Comment thread
microdee marked this conversation as resolved.
Outdated
}
return result;

} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
return static_cast<T>(_var.node_.as<std::remove_cvref_t<int64_t>>());
Expand Down
40 changes: 31 additions & 9 deletions include/rfl/yaml/Writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ namespace rfl::yaml {

class RFL_API Writer {
public:
enum Flags {
no_flags = 0,

/// A string value which has at least one new-line character will be written
/// as multiline YAML literal. It costs one call to std::basic_string::find
/// on all string values.
string_multiline_literal = 1,

/// All string values will be written as multiline YAML literal
string_all_literal = 2
};

struct YAMLArray {};

struct YAMLObject {};
Expand All @@ -25,7 +37,7 @@ class RFL_API Writer {
using OutputObjectType = YAMLObject;
using OutputVarType = YAMLVar;

Writer(const Ref<YAML::Emitter>& _out);
Writer(const Ref<YAML::Emitter>& _out, Flags _flags = no_flags);

~Writer();

Expand Down Expand Up @@ -85,10 +97,15 @@ class RFL_API Writer {
template <class T>
OutputVarType insert_value(const std::string_view& _name,
const T& _var) const {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>() ||
std::is_same<std::remove_cvref_t<T>, bool>() ||
std::is_same<std::remove_cvref_t<T>,
std::remove_cvref_t<decltype(YAML::Null)>>()) {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
(*out_) << YAML::Key << std::string(_name) << YAML::Value;
if (flags & string_all_literal || (flags & string_multiline_literal && _var.find('\n') != std::string::npos)) {
(*out_) << YAML::Literal;
}
Comment thread
microdee marked this conversation as resolved.
Outdated
(*out_) << _var;
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>() ||
std::is_same<std::remove_cvref_t<T>,
std::remove_cvref_t<decltype(YAML::Null)>>()) {
(*out_) << YAML::Key << std::string(_name) << YAML::Value << _var;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
// std::to_string is necessary to ensure that floating point values are
Expand All @@ -106,10 +123,14 @@ class RFL_API Writer {

template <class T>
OutputVarType insert_value(const T& _var) const {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>() ||
std::is_same<std::remove_cvref_t<T>, bool>() ||
std::is_same<std::remove_cvref_t<T>,
std::remove_cvref_t<decltype(YAML::Null)>>()) {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
if (flags & string_all_literal || (flags & string_multiline_literal && _var.find('\n') != std::string::npos)) {
(*out_) << YAML::Literal;
}
(*out_) << _var;
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>() ||
std::is_same<std::remove_cvref_t<T>,
std::remove_cvref_t<decltype(YAML::Null)>>()) {
(*out_) << _var;
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
// std::to_string is necessary to ensure that floating point values are
Expand All @@ -135,6 +156,7 @@ class RFL_API Writer {

public:
const Ref<YAML::Emitter> out_;
Flags flags;
};

} // namespace rfl::yaml
Expand Down
8 changes: 4 additions & 4 deletions include/rfl/yaml/write.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ namespace yaml {

/// Writes a YAML into an ostream.
template <class... Ps>
std::ostream& write(const auto& _obj, std::ostream& _stream) {
std::ostream& write(const auto& _obj, std::ostream& _stream, Writer::Flags _flags = Writer::Flags::no_flags) {
using T = std::remove_cvref_t<decltype(_obj)>;
using ParentType = parsing::Parent<Writer>;
const auto out = Ref<YAML::Emitter>::make();
auto w = Writer(out);
auto w = Writer(out, _flags);
using ProcessorsType = Processors<Ps...>;
static_assert(!ProcessorsType::no_field_names_,
"The NoFieldNames processor is not supported for BSON, XML, "
Expand All @@ -33,11 +33,11 @@ std::ostream& write(const auto& _obj, std::ostream& _stream) {

/// Returns a YAML string.
template <class... Ps>
std::string write(const auto& _obj) {
std::string write(const auto& _obj, Writer::Flags _flags = Writer::Flags::no_flags) {
using T = std::remove_cvref_t<decltype(_obj)>;
using ParentType = parsing::Parent<Writer>;
const auto out = Ref<YAML::Emitter>::make();
auto w = Writer(out);
auto w = Writer(out, _flags);
using ProcessorsType = Processors<Ps...>;
static_assert(!ProcessorsType::no_field_names_,
"The NoFieldNames processor is not supported for BSON, XML, "
Expand Down
2 changes: 1 addition & 1 deletion src/rfl/yaml/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace rfl::yaml {

Writer::Writer(const Ref<YAML::Emitter>& _out) : out_(_out) {}
Writer::Writer(const Ref<YAML::Emitter>& _out, Flags _flags) : out_(_out), flags(_flags) {}

Writer::~Writer() = default;

Expand Down
52 changes: 52 additions & 0 deletions tests/yaml/test_multiline.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <rfl.hpp>
#include <string>

#include "write_and_read.hpp"

struct MultilineTestStruct {
std::string normal_string;
std::string multiline_string;
};

namespace test_multiline {
TEST(yaml, test_multiline) {
const auto test = MultilineTestStruct{.normal_string = "The normal string",
.multiline_string =
R"(Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.)"
};

write_and_read(test, rfl::yaml::Writer::string_multiline_literal);
write_and_read(test, rfl::yaml::Writer::string_all_literal);
}

TEST(yaml, test_multiline_read) {
const auto test = MultilineTestStruct{.normal_string = "The normal string",
.multiline_string =
R"(Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, etc...)"
};

const std::string random_yaml(
R"(
normal_string: |
The normal string


multiline_string: |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, etc...

)"
);

auto read_result = rfl::yaml::read<MultilineTestStruct>(random_yaml);
EXPECT_TRUE(read_result.has_value());
EXPECT_EQ(read_result.value().normal_string, test.normal_string);
EXPECT_EQ(read_result.value().multiline_string, test.multiline_string);
}
} // namespace test_multiline
6 changes: 3 additions & 3 deletions tests/yaml/write_and_read.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
#include <rfl/yaml.hpp>

template <class... Ps>
void write_and_read(const auto& _struct) {
void write_and_read(const auto& _struct, rfl::yaml::Writer::Flags _flags = rfl::yaml::Writer::Flags::no_flags) {
using T = std::remove_cvref_t<decltype(_struct)>;
const auto serialized1 = rfl::yaml::write<Ps...>(_struct);
const auto serialized1 = rfl::yaml::write<Ps...>(_struct, _flags);
const auto res = rfl::yaml::read<T, Ps...>(
std::string_view(serialized1.c_str(), serialized1.size()));
EXPECT_TRUE(res && true) << "Test failed on read. Error: "
<< res.error().what();
const auto serialized2 = rfl::yaml::write<Ps...>(res.value());
const auto serialized2 = rfl::yaml::write<Ps...>(res.value(), _flags);
EXPECT_EQ(serialized1, serialized2);
}

Expand Down