Add option to preserve node marks when cloning a node
This commit is contained in:
parent
7470c2d871
commit
5eeac8a240
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
92
test/integration/clone_node_test.cpp
Normal file
92
test/integration/clone_node_test.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user