Skip to content

Commit

Permalink
Merge pull request #75 from fktn-k/feature/5_implement_serialize_yaml…
Browse files Browse the repository at this point in the history
…_nodes

#5 implemented yaml node serializer
  • Loading branch information
fktn-k committed Sep 16, 2023
2 parents fbc4d15 + 92712fe commit e886233
Show file tree
Hide file tree
Showing 3 changed files with 262 additions and 0 deletions.
153 changes: 153 additions & 0 deletions include/fkYAML/Serializer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* @file Deserializer.hpp
* @brief Implementation of the serializer for YAML nodes.
*
* Copyright (c) 2023 fktn
* Distributed under the MIT License (https://opensource.org/licenses/MIT)
*/

#ifndef FK_YAML_SERIALIZER_HPP_
#define FK_YAML_SERIALIZER_HPP_

#include <cmath>
#include <sstream>
#include <string>

#include "fkYAML/VersioningMacros.hpp"
#include "fkYAML/Exception.hpp"
#include "fkYAML/Node.hpp"
#include "fkYAML/NodeType.hpp"
#include "fkYAML/NodeTypeTraits.hpp"

FK_YAML_NAMESPACE_BEGIN

template <typename BasicNodeType = Node>
class BasicSerializer
{
static_assert(IsBasicNode<BasicNodeType>::value, "BasicSerializer only accepts (const) BasicNode<...>");

public:
/**
* @brief Construct a new BasicSerializer object.
*/
BasicSerializer() = default;

std::string Serialize(BasicNodeType& node)
{
std::string str {};
SerializeNode(node, 0, str);
return str;
}

private:
void SerializeNode(BasicNodeType& node, const uint32_t cur_indent, std::string& str, bool is_seq_item = false)
{
switch (node.Type())
{
case NodeType::SEQUENCE:
for (auto& seq_item : node)
{
InsertIndentation(cur_indent, str);
str += "-";
if (seq_item.IsScalar())
{
str += " ";
SerializeNode(seq_item, cur_indent, str, true);
str += "\n";
}
else
{
str += "\n";
SerializeNode(seq_item, cur_indent + 2, str, true);
}
}
break;
case NodeType::MAPPING:
for (auto itr = node.Begin(); itr != node.End(); ++itr)
{
InsertIndentation(cur_indent, str);
SerializeKey(itr.Key(), cur_indent, str);
if (itr->IsScalar())
{
str += " ";
SerializeNode(*itr, cur_indent, str);
str += "\n";
}
else
{
str += "\n";
SerializeNode(*itr, cur_indent + 2, str);
}
}
break;
case NodeType::NULL_OBJECT:
str += "null";
break;
case NodeType::BOOLEAN:
if (node.ToBoolean())
{
str += "true";
}
else
{
str += "false";
}
break;
case NodeType::SIGNED_INTEGER:
str += std::to_string(node.ToSignedInteger());
break;
case NodeType::UNSIGNED_INTEGER:
str += std::to_string(node.ToUnsignedInteger());
break;
case NodeType::FLOAT_NUMBER: {
typename BasicNodeType::float_number_type float_val = node.ToFloatNumber();
if (std::isnan(float_val))
{
str += ".nan";
}
else if (std::isinf(float_val))
{
if (float_val == std::numeric_limits<typename BasicNodeType::float_number_type>::infinity())
{
str += ".inf";
}
else
{
str += "-.inf";
}
}
else
{
std::stringstream ss;
ss << node.ToFloatNumber();
str += ss.str();
}
break;
}
case NodeType::STRING:
str += node.ToString();
break;
default:
throw Exception("Unsupported node type found.");
}
}

void SerializeKey(const std::string& key, const uint32_t cur_indent, std::string& str)
{
str += key + ":";
}

void InsertIndentation(const uint32_t cur_indent, std::string& str)
{
for (uint32_t i = 0; i < cur_indent; ++i)
{
str += " ";
}
}
};

using Serializer = BasicSerializer<>;

FK_YAML_NAMESPACE_END

#endif /* FK_YAML_SERIALIZER_HPP_ */
1 change: 1 addition & 0 deletions test/unit_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ add_executable(${TEST_TARGET}
IteratorClassTest.cpp
LexicalAnalyzerClassTest.cpp
DeserializerClassTest.cpp
SerializerClassTest.cpp
main.cpp
)

Expand Down
108 changes: 108 additions & 0 deletions test/unit_test/SerializerClassTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* SerializerClassTest.cpp - implementation of test functions for the Serializer class
*
* Copyright (c) 2023 fktn
* Distributed under the MIT License (https://opensource.org/licenses/MIT)
*/

#include "catch2/catch.hpp"

#include <limits>

#include "fkYAML/Node.hpp"
#include "fkYAML/Serializer.hpp"

TEST_CASE("SerializerClassTest_SerializeSequenceNode", "[SerializerClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(
fkyaml::Node::Sequence({fkyaml::Node::BooleanScalar(true), fkyaml::Node::BooleanScalar(false)}),
"- true\n- false\n"),
NodeStrPair(
fkyaml::Node::Sequence(
{fkyaml::Node::Mapping({{"foo", fkyaml::Node::SignedIntegerScalar(-1234)}, {"bar", fkyaml::Node()}})}),
"-\n bar: null\n foo: -1234\n")); // FIXME: keep insertion order.
fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

TEST_CASE("SerializerClassTest_SerializeMappingNode", "[SerializerClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(
fkyaml::Node::Mapping({{"foo", fkyaml::Node::SignedIntegerScalar(-1234)}, {"bar", fkyaml::Node()}}),
"bar: null\nfoo: -1234\n"), // FIXME: keep insertion order.
NodeStrPair(
fkyaml::Node::Mapping(
{{"foo",
fkyaml::Node::Sequence({fkyaml::Node::BooleanScalar(true), fkyaml::Node::BooleanScalar(false)})}}),
"foo:\n - true\n - false\n"));
fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

TEST_CASE("SerializerClassTest_SerializeNullNode", "[SerializerClassTest]")
{
fkyaml::Serializer serializer;
fkyaml::Node node;
REQUIRE(serializer.Serialize(node) == "null");
}

TEST_CASE("SerializerClassTest_SerializeBooleanNode", "[SerializerClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(fkyaml::Node::BooleanScalar(false), "false"),
NodeStrPair(fkyaml::Node::BooleanScalar(true), "true"));
fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

TEST_CASE("SerializerClassTest_SerializeSignedIntegerNode", "[SerializerClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(fkyaml::Node::SignedIntegerScalar(-1234), "-1234"),
NodeStrPair(fkyaml::Node::SignedIntegerScalar(5678), "5678"));
fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

TEST_CASE("SerializeClassTest_SerializeUnsignedIntegerNode", "[SerializeClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(fkyaml::Node::UnsignedIntegerScalar(1234), "1234"),
NodeStrPair(fkyaml::Node::UnsignedIntegerScalar(5678), "5678"));
fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

TEST_CASE("SerializeClassTest_SerializeFloatNumberNode", "[SerializeClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(fkyaml::Node::FloatNumberScalar(3.14), "3.14"),
NodeStrPair(fkyaml::Node::FloatNumberScalar(-53.97), "-53.97"),
NodeStrPair(
fkyaml::Node::FloatNumberScalar(std::numeric_limits<fkyaml::NodeFloatNumberType>::infinity()), ".inf"),
NodeStrPair(
fkyaml::Node::FloatNumberScalar(-1 * std::numeric_limits<fkyaml::NodeFloatNumberType>::infinity()),
"-.inf"),
NodeStrPair(fkyaml::Node::FloatNumberScalar(std::nan("")), ".nan"));
fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

TEST_CASE("SerializerClassTest_SerializeStringNode", "[SerializerClassTest]")
{
using NodeStrPair = std::pair<fkyaml::Node, std::string>;
auto node_str_pair = GENERATE(
NodeStrPair(fkyaml::Node::StringScalar("test"), "test"),
NodeStrPair(fkyaml::Node::StringScalar("foo bar"), "foo bar"));

fkyaml::Serializer serializer;
REQUIRE(serializer.Serialize(node_str_pair.first) == node_str_pair.second);
}

0 comments on commit e886233

Please sign in to comment.