This commit is contained in:
Vadim D. 2025-01-28 04:51:50 +00:00 committed by GitHub
commit a2292058a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 104 additions and 16 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 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> template <typename T>
struct convert; struct convert;

View File

@ -3,10 +3,10 @@
#include "nodeevents.h" #include "nodeevents.h"
namespace YAML { namespace YAML {
Node Clone(const Node& node) { Node Clone(const Node& node, bool preserveMarks) {
NodeEvents events(node); NodeEvents events(node);
NodeBuilder builder; NodeBuilder builder;
events.Emit(builder); events.Emit(builder, preserveMarks);
return builder.Root(); return builder.Root();
} }
} // namespace YAML } // 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; AliasManager am;
handler.OnDocumentStart(Mark()); handler.OnDocumentStart(Mark());
if (m_root) if (m_root)
Emit(*m_root, handler, am); Emit(*m_root, handler, am, preserveMarks);
handler.OnDocumentEnd(); handler.OnDocumentEnd();
} }
void NodeEvents::Emit(const detail::node& node, EventHandler& handler, void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
AliasManager& am) const { AliasManager& am, bool preserveMarks) const {
anchor_t anchor = NullAnchor; anchor_t anchor = NullAnchor;
if (IsAliased(node)) { if (IsAliased(node)) {
anchor = am.LookupAnchor(node); anchor = am.LookupAnchor(node);
@ -65,26 +65,30 @@ void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
anchor = am.LookupAnchor(node); anchor = am.LookupAnchor(node);
} }
auto getMark = [preserveMarks, &node]() {
return preserveMarks ? node.mark() : Mark();
};
switch (node.type()) { switch (node.type()) {
case NodeType::Undefined: case NodeType::Undefined:
break; break;
case NodeType::Null: case NodeType::Null:
handler.OnNull(Mark(), anchor); handler.OnNull(getMark(), anchor);
break; break;
case NodeType::Scalar: case NodeType::Scalar:
handler.OnScalar(Mark(), node.tag(), anchor, node.scalar()); handler.OnScalar(getMark(), node.tag(), anchor, node.scalar());
break; break;
case NodeType::Sequence: case NodeType::Sequence:
handler.OnSequenceStart(Mark(), node.tag(), anchor, node.style()); handler.OnSequenceStart(getMark(), node.tag(), anchor, node.style());
for (auto element : node) for (auto element : node)
Emit(*element, handler, am); Emit(*element, handler, am, preserveMarks);
handler.OnSequenceEnd(); handler.OnSequenceEnd();
break; break;
case NodeType::Map: case NodeType::Map:
handler.OnMapStart(Mark(), node.tag(), anchor, node.style()); handler.OnMapStart(getMark(), node.tag(), anchor, node.style());
for (auto element : node) { for (auto element : node) {
Emit(*element.first, handler, am); Emit(*element.first, handler, am, preserveMarks);
Emit(*element.second, handler, am); Emit(*element.second, handler, am, preserveMarks);
} }
handler.OnMapEnd(); handler.OnMapEnd();
break; break;

View File

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

View File

@ -0,0 +1,84 @@
#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); }
} // namespace YAML
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);
}
}