Add option to preserve node marks when cloning a node

This commit is contained in:
Vadim D 2024-12-30 22:47:32 +03:00
parent 7470c2d871
commit 5eeac8a240
5 changed files with 111 additions and 15 deletions

View File

@ -139,7 +139,7 @@ class YAML_CPP_API Node {
YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs);
YAML_CPP_API Node Clone(const Node& node);
YAML_CPP_API Node Clone(const Node& node, bool preserveMarks = false);
template <typename T>
struct convert;

View File

@ -3,10 +3,10 @@
#include "nodeevents.h"
namespace YAML {
Node Clone(const Node& node) {
Node Clone(const Node& node, bool preserveMarks) {
NodeEvents events(node);
NodeBuilder builder;
events.Emit(builder);
events.Emit(builder, preserveMarks);
return builder.Root();
}
} // namespace YAML

View File

@ -42,17 +42,17 @@ void NodeEvents::Setup(const detail::node& node) {
}
}
void NodeEvents::Emit(EventHandler& handler) {
void NodeEvents::Emit(EventHandler& handler, bool preserveMarks) {
AliasManager am;
handler.OnDocumentStart(Mark());
if (m_root)
Emit(*m_root, handler, am);
Emit(*m_root, handler, am, preserveMarks);
handler.OnDocumentEnd();
}
void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
AliasManager& am) const {
AliasManager& am, bool preserveMarks) const {
anchor_t anchor = NullAnchor;
if (IsAliased(node)) {
anchor = am.LookupAnchor(node);
@ -65,26 +65,30 @@ void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
anchor = am.LookupAnchor(node);
}
auto getMark = [preserveMarks, &node]() {
return preserveMarks ? node.mark() : Mark();
};
switch (node.type()) {
case NodeType::Undefined:
break;
case NodeType::Null:
handler.OnNull(Mark(), anchor);
handler.OnNull(getMark(), anchor);
break;
case NodeType::Scalar:
handler.OnScalar(Mark(), node.tag(), anchor, node.scalar());
handler.OnScalar(getMark(), node.tag(), anchor, node.scalar());
break;
case NodeType::Sequence:
handler.OnSequenceStart(Mark(), node.tag(), anchor, node.style());
handler.OnSequenceStart(getMark(), node.tag(), anchor, node.style());
for (auto element : node)
Emit(*element, handler, am);
Emit(*element, handler, am, preserveMarks);
handler.OnSequenceEnd();
break;
case NodeType::Map:
handler.OnMapStart(Mark(), node.tag(), anchor, node.style());
handler.OnMapStart(getMark(), node.tag(), anchor, node.style());
for (auto element : node) {
Emit(*element.first, handler, am);
Emit(*element.second, handler, am);
Emit(*element.first, handler, am, preserveMarks);
Emit(*element.second, handler, am, preserveMarks);
}
handler.OnMapEnd();
break;

View File

@ -31,7 +31,7 @@ class NodeEvents {
NodeEvents& operator=(const NodeEvents&) = delete;
NodeEvents& operator=(NodeEvents&&) = delete;
void Emit(EventHandler& handler);
void Emit(EventHandler& handler, bool preserveMarks = false);
private:
class AliasManager {
@ -53,7 +53,7 @@ class NodeEvents {
void Setup(const detail::node& node);
void Emit(const detail::node& node, EventHandler& handler,
AliasManager& am) const;
AliasManager& am, bool preserveMarks) const;
bool IsAliased(const detail::node& node) const;
private:

View File

@ -0,0 +1,92 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <yaml-cpp/yaml.h>
struct ComparableMark {
int pos;
int line, column;
ComparableMark(int pos, int line, int column)
: pos(pos), line(line), column(column) {}
};
bool operator==(const YAML::Mark& a, const ComparableMark& b) {
return a.pos == b.pos && a.line == b.line && a.column == b.column;
}
template <typename Mark>
void PrintMark(const Mark& mark, std::ostream* os) {
*os << mark.line << ':' << mark.column << " (pos " << mark.pos << ')';
}
void PrintTo(const ComparableMark& mark, std::ostream* os) {
PrintMark(mark, os);
}
namespace YAML {
void PrintTo(const Mark& mark, std::ostream* os) {
PrintMark(mark, os);
}
}
TEST(CloneNodeTest, PreserveMark) {
std::string yaml_str = R"(
scalar: value
sequence: [1, 2, 3]
"null": null
[1, 2, 3]: value # check non-scalar keys
)";
auto checkMarks = [](const YAML::Node& root_node) {
EXPECT_EQ(root_node.Mark(), ComparableMark(1, 1, 0));
const YAML::Node& scalar = root_node["scalar"];
EXPECT_EQ(scalar.Mark(), ComparableMark(9, 1, 8));
const YAML::Node& sequence = root_node["sequence"];
EXPECT_EQ(sequence.Mark(), ComparableMark(25, 2, 10));
EXPECT_EQ(sequence[0].Mark(), ComparableMark(26, 2, 11));
EXPECT_EQ(sequence[1].Mark(), ComparableMark(29, 2, 14));
EXPECT_EQ(sequence[2].Mark(), ComparableMark(32, 2, 17));
const YAML::Node& null = root_node["null"];
EXPECT_EQ(null.Mark(), ComparableMark(43, 3, 8));
YAML::Node sequence_key;
std::vector<YAML::Mark> key_marks;
for (auto it = root_node.begin(); it != root_node.end(); ++it) {
// Not assuming any key order
key_marks.emplace_back(it->first.Mark());
if (it->first.IsSequence()) {
sequence_key.reset(it->first);
}
}
EXPECT_THAT(
key_marks,
testing::UnorderedElementsAre(
ComparableMark(1, 1, 0),
ComparableMark(15, 2, 0),
ComparableMark(35, 3, 0),
ComparableMark(48, 4, 0)
)
);
ASSERT_TRUE(sequence_key);
EXPECT_EQ(sequence_key[0].Mark(), ComparableMark(49, 4, 1));
EXPECT_EQ(sequence_key[1].Mark(), ComparableMark(52, 4, 4));
EXPECT_EQ(sequence_key[2].Mark(), ComparableMark(55, 4, 7));
};
YAML::Node root_node = YAML::Load(yaml_str);
{
SCOPED_TRACE("original node");
checkMarks(root_node);
}
YAML::Node cloned_node = YAML::Clone(root_node, true);
{
SCOPED_TRACE("cloned node");
checkMarks(cloned_node);
}
}