Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
39 changes: 1 addition & 38 deletions bundler/extension_refs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,49 +67,12 @@ func resolveExtensionRefContent(ctx context.Context, ref *index.Reference, _ *in
foundRef := ref.Index.FindComponent(ctx, ref.FullDefinition)
if foundRef != nil && foundRef.Node != nil {
// Deep copy to avoid mutating original component
return deepCopyNode(foundRef.Node)
return utils.CloneYAMLNodeWithFlags(foundRef.Node, utils.YAMLNodeCloneUnwrapDocument)
}
}
return nil
}

// deepCopyNode creates a deep copy of a yaml.Node tree.
func deepCopyNode(node *yaml.Node) *yaml.Node {
if node == nil {
return nil
}

// unwrap document nodes
if node.Kind == yaml.DocumentNode && len(node.Content) > 0 {
node = node.Content[0]
}

// create copy
nodeCopy := &yaml.Node{
Kind: node.Kind,
Style: node.Style,
Tag: node.Tag,
Value: node.Value,
Anchor: node.Anchor,
Alias: node.Alias,
HeadComment: node.HeadComment,
LineComment: node.LineComment,
FootComment: node.FootComment,
Line: node.Line,
Column: node.Column,
}

// deep copy children
if len(node.Content) > 0 {
nodeCopy.Content = make([]*yaml.Node, len(node.Content))
for i, child := range node.Content {
nodeCopy.Content[i] = deepCopyNode(child)
}
}

return nodeCopy
}

func replaceRefNodeWithContent(refNode, content *yaml.Node) {
if refNode == nil || content == nil {
return
Expand Down
129 changes: 0 additions & 129 deletions bundler/extension_refs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,135 +305,6 @@ func TestResolveExtensionRefContent_ComponentNotFound(t *testing.T) {
}
}

func TestDeepCopyNode_Nil(t *testing.T) {
result := deepCopyNode(nil)
if result != nil {
t.Error("Expected nil result for nil input")
}
}

func TestDeepCopyNode_DocumentNode(t *testing.T) {
// Test unwrapping of DocumentNode
innerNode := &yaml.Node{
Kind: yaml.ScalarNode,
Value: "test",
}
docNode := &yaml.Node{
Kind: yaml.DocumentNode,
Content: []*yaml.Node{innerNode},
}

result := deepCopyNode(docNode)
if result == nil {
t.Fatal("Expected non-nil result")
}
if result.Kind != yaml.ScalarNode {
t.Errorf("Expected ScalarNode after unwrap, got %v", result.Kind)
}
if result.Value != "test" {
t.Errorf("Expected value 'test', got '%s'", result.Value)
}
}

func TestDeepCopyNode_WithContent(t *testing.T) {
// Test deep copy with children
child1 := &yaml.Node{Kind: yaml.ScalarNode, Value: "key"}
child2 := &yaml.Node{Kind: yaml.ScalarNode, Value: "value"}
parent := &yaml.Node{
Kind: yaml.MappingNode,
Content: []*yaml.Node{child1, child2},
}

result := deepCopyNode(parent)
if result == nil {
t.Fatal("Expected non-nil result")
}
if len(result.Content) != 2 {
t.Fatalf("Expected 2 children, got %d", len(result.Content))
}
// Verify it's a deep copy (different pointers)
if result.Content[0] == child1 {
t.Error("Expected deep copy, got same pointer for child1")
}
if result.Content[1] == child2 {
t.Error("Expected deep copy, got same pointer for child2")
}
// Verify values are copied
if result.Content[0].Value != "key" {
t.Errorf("Expected 'key', got '%s'", result.Content[0].Value)
}
if result.Content[1].Value != "value" {
t.Errorf("Expected 'value', got '%s'", result.Content[1].Value)
}
}

func TestDeepCopyNode_NoContent(t *testing.T) {
// Test node with no children
node := &yaml.Node{
Kind: yaml.ScalarNode,
Value: "scalar",
}

result := deepCopyNode(node)
if result == nil {
t.Fatal("Expected non-nil result")
}
if result.Value != "scalar" {
t.Errorf("Expected 'scalar', got '%s'", result.Value)
}
if result.Content != nil {
t.Error("Expected nil Content for scalar node")
}
}

func TestDeepCopyNode_AllFields(t *testing.T) {
// Test that all fields are copied
node := &yaml.Node{
Kind: yaml.ScalarNode,
Style: yaml.DoubleQuotedStyle,
Tag: "!!str",
Value: "test",
Anchor: "anchor1",
HeadComment: "head",
LineComment: "line",
FootComment: "foot",
Line: 10,
Column: 5,
}

result := deepCopyNode(node)
if result.Kind != yaml.ScalarNode {
t.Errorf("Kind mismatch")
}
if result.Style != yaml.DoubleQuotedStyle {
t.Errorf("Style mismatch")
}
if result.Tag != "!!str" {
t.Errorf("Tag mismatch")
}
if result.Value != "test" {
t.Errorf("Value mismatch")
}
if result.Anchor != "anchor1" {
t.Errorf("Anchor mismatch")
}
if result.HeadComment != "head" {
t.Errorf("HeadComment mismatch")
}
if result.LineComment != "line" {
t.Errorf("LineComment mismatch")
}
if result.FootComment != "foot" {
t.Errorf("FootComment mismatch")
}
if result.Line != 10 {
t.Errorf("Line mismatch")
}
if result.Column != 5 {
t.Errorf("Column mismatch")
}
}

func TestReplaceRefNodeWithContent_NilRefNode(t *testing.T) {
content := &yaml.Node{Kind: yaml.ScalarNode, Value: "test"}
// Should not panic with nil refNode
Expand Down
36 changes: 5 additions & 31 deletions datamodel/high/base/schema_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,8 +540,8 @@ func (sp *SchemaProxy) renderTransformedRefWithSiblings(s *Schema) (*yaml.Node,
continue
}
if keyNode.Value == "$ref" {
refKey := cloneYAMLNode(keyNode)
refValue := cloneYAMLNode(valueNode)
refKey := utils.CloneYAMLNode(keyNode)
refValue := utils.CloneYAMLNode(valueNode)
if refValue == nil {
refValue = utils.CreateStringNode(ref)
}
Expand All @@ -550,8 +550,8 @@ func (sp *SchemaProxy) renderTransformedRefWithSiblings(s *Schema) (*yaml.Node,
continue
}
if _, siblingValue := findYAMLPair(siblingNode, keyNode.Value); siblingValue != nil {
renderKey := cloneYAMLNode(keyNode)
result.Content = append(result.Content, renderKey, cloneYAMLNode(siblingValue))
renderKey := utils.CloneYAMLNode(keyNode)
result.Content = append(result.Content, renderKey, utils.CloneYAMLNode(siblingValue))
consumed[keyNode.Value] = struct{}{}
}
}
Expand All @@ -562,7 +562,7 @@ func (sp *SchemaProxy) renderTransformedRefWithSiblings(s *Schema) (*yaml.Node,
if _, ok := consumed[keyNode.Value]; ok {
continue
}
result.Content = append(result.Content, cloneYAMLNode(keyNode), cloneYAMLNode(valueNode))
result.Content = append(result.Content, utils.CloneYAMLNode(keyNode), utils.CloneYAMLNode(valueNode))
}

return result, true, nil
Expand Down Expand Up @@ -609,32 +609,6 @@ func findYAMLPair(node *yaml.Node, key string) (*yaml.Node, *yaml.Node) {
return nil, nil
}

func cloneYAMLNode(node *yaml.Node) *yaml.Node {
if node == nil {
return nil
}
clone := &yaml.Node{
Kind: node.Kind,
Style: node.Style,
Tag: node.Tag,
Value: node.Value,
Anchor: node.Anchor,
Alias: node.Alias,
Line: node.Line,
Column: node.Column,
HeadComment: node.HeadComment,
LineComment: node.LineComment,
FootComment: node.FootComment,
}
if len(node.Content) > 0 {
clone.Content = make([]*yaml.Node, len(node.Content))
for i, child := range node.Content {
clone.Content[i] = cloneYAMLNode(child)
}
}
return clone
}

// Render will return a YAML representation of the Schema object as a byte slice.
func (sp *SchemaProxy) Render() ([]byte, error) {
return yaml.Marshal(sp)
Expand Down
4 changes: 2 additions & 2 deletions datamodel/high/base/schema_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2194,8 +2194,8 @@ func TestTransformedRefRenderHelpers(t *testing.T) {
assert.Nil(t, key)
assert.Nil(t, value)

assert.Nil(t, cloneYAMLNode(nil))
cloned := cloneYAMLNode(mapping)
assert.Nil(t, utils.CloneYAMLNode(nil))
cloned := utils.CloneYAMLNode(mapping)
require.NotNil(t, cloned)
assert.Equal(t, mapping.Content[0].Value, cloned.Content[0].Value)
cloned.Content[0].Value = "changed"
Expand Down
19 changes: 1 addition & 18 deletions datamodel/high/node_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,28 +356,11 @@ func (n *NodeBuilder) Render() *yaml.Node {
// the same node. Encoding a copy keeps shared nodes immutable.
func encodeSafeValue(value any) any {
if vn, ok := value.(*yaml.Node); ok {
return deepCopyYAMLNode(vn)
return utils.CloneYAMLNode(vn)
}
return value
}

func deepCopyYAMLNode(n *yaml.Node) *yaml.Node {
if n == nil {
return nil
}
c := *n
if n.Alias != nil {
c.Alias = deepCopyYAMLNode(n.Alias)
}
if n.Content != nil {
c.Content = make([]*yaml.Node, len(n.Content))
for i, child := range n.Content {
c.Content[i] = deepCopyYAMLNode(child)
}
}
return &c
}

// AddYAMLNode will add a new *yaml.Node to the parent node, using the tag, key and value provided.
// If the value is nil, then the node will not be added. This method is recursive, so it will dig down
// into any non-scalar types.
Expand Down
1 change: 0 additions & 1 deletion datamodel/high/node_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1707,7 +1707,6 @@ func TestEncodeSafeValue_DeepCopiesYAMLNodes(t *testing.T) {
assert.Equal(t, "hello", encodeSafeValue("hello"))

// nil nodes return nil rather than panicking.
assert.Nil(t, deepCopyYAMLNode(nil))
assert.Nil(t, encodeSafeValue((*yaml.Node)(nil)))

// a *yaml.Node is deep-copied: every node is a distinct pointer, including
Expand Down
19 changes: 1 addition & 18 deletions datamodel/high/v3/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ func preserveComponentRefEntries[T any](
sectionNode,
)
}
upsertMapNodeEntry(sectionNode, cloneYAMLNode(keyNode), cloneYAMLNode(valueNode))
upsertMapNodeEntry(sectionNode, utils.CloneYAMLNode(keyNode), utils.CloneYAMLNode(valueNode))
}
}

Expand Down Expand Up @@ -346,20 +346,3 @@ func upsertMapNodeEntry(m *yaml.Node, keyNode, valueNode *yaml.Node) {
}
m.Content = append(m.Content, keyNode, valueNode)
}

// cloneYAMLNode deep-copies a YAML node tree so preserved low-level nodes can be spliced into
// rendered output without mutating the original parsed model.
func cloneYAMLNode(node *yaml.Node) *yaml.Node {
if node == nil {
return nil
}

cloned := *node
if len(node.Content) > 0 {
cloned.Content = make([]*yaml.Node, len(node.Content))
for i, child := range node.Content {
cloned.Content[i] = cloneYAMLNode(child)
}
}
return &cloned
}
19 changes: 0 additions & 19 deletions datamodel/high/v3/components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,25 +478,6 @@ func TestFindMapValueNodeAndUpsertMapNodeEntry(t *testing.T) {
assert.Equal(t, "./updated.yaml", rendered.Content[1].Value)
}

func TestCloneYAMLNode_ClonesRecursively(t *testing.T) {
assert.Nil(t, cloneYAMLNode(nil))

original := utils.CreateEmptyMapNode()
original.Content = append(
original.Content,
utils.CreateStringNode("child"),
utils.CreateStringNode("value"),
)

cloned := cloneYAMLNode(original)
require.NotNil(t, cloned)
require.NotSame(t, original, cloned)
require.Len(t, cloned.Content, 2)
assert.NotSame(t, original.Content[0], cloned.Content[0])
assert.Equal(t, "child", cloned.Content[0].Value)
assert.Equal(t, "value", cloned.Content[1].Value)
}

func setLowComponentsIndex(comp *v3.Components, idx *index.SpecIndex) {
field := reflect.ValueOf(comp).Elem().FieldByName("index")
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Set(reflect.ValueOf(idx))
Expand Down
Loading
Loading