Repository: TomWright/dasel
Pull request #416 (2024-06-30 14:21:39)

handle non-merge aliasing
@TomWright my patch in #415 is incorrect and is missing a piece:

1. Regular alias dereferencing, e.g. `foo: *bar` was not handled, only merging an alias into a map.
2. Merging an alias into a map is incorrect in #415 as it should only merge the children of the aliased map rather then the aliased map itself. For example,
    
    ```yaml
    foo: &foo
      bar: 1
      baz: "baz"
    
    spam:
      <<: *foo
    ```
    
    currently decodes into
    
    ```yaml
    foo: &foo
      bar: 1
      baz: "baz"
    
    spam:
      foo: foo
        bar: 1
        baz: "baz"
    ```
    
    but should decode into
    
    ```yaml
    foo: &foo
      bar: 1
      baz: "baz"
    
    spam:
      bar: 1
      baz: "baz"
    ```

TomWright: Thanks for picking this up @pmeier, I'll release it under `v2.8.1` shortly

Patch:
diff --git a/dencoding/yaml_decoder.go b/dencoding/yaml_decoder.go
index d868cf3..ce1975f 100644
--- a/dencoding/yaml_decoder.go
+++ b/dencoding/yaml_decoder.go
@@ -64,6 +64,8 @@ func (decoder *YAMLDecoder) getNodeValue(node *yaml.Node) (any, error) {
 		return decoder.getSequenceNodeValue(node)
 	case yaml.ScalarNode:
 		return decoder.getScalarNodeValue(node)
+	case yaml.AliasNode:
+		return decoder.getNodeValue(node.Alias)
 	default:
 		return nil, fmt.Errorf("unhandled node kind: %v", node.Kind)
 	}
@@ -72,20 +74,26 @@ func (decoder *YAMLDecoder) getNodeValue(node *yaml.Node) (any, error) {
 func (decoder *YAMLDecoder) getMappingNodeValue(node *yaml.Node) (any, error) {
 	res := NewMap()
 
-	for i := 0; i < len(node.Content); i += 2 {
-		keyNode := node.Content[i]
-		valueNode := node.Content[i+1]
+	content := make([]*yaml.Node, 0)
+	content = append(content, node.Content...)
+
+	var keyNode *yaml.Node
+	var valueNode *yaml.Node
+	for {
+		if len(content) == 0 {
+			break
+		}
+
+		keyNode, valueNode, content = content[0], content[1], content[2:]
 
-		var keyValue any
 		if keyNode.ShortTag() == yamlTagMerge {
-			keyValue = valueNode.Value
-			valueNode = valueNode.Alias
-		} else {
-			var err error
-			keyValue, err = decoder.getNodeValue(keyNode)
-			if err != nil {
-				return nil, err
-			}
+			content = append(valueNode.Alias.Content, content...)
+			continue
+		}
+
+		keyValue, err := decoder.getNodeValue(keyNode)
+		if err != nil {
+			return nil, err
 		}
 
 		value, err := decoder.getNodeValue(valueNode)


Test patch:
diff --git a/dencoding/yaml_decoder_test.go b/dencoding/yaml_decoder_test.go
index 34679bb..172bc53 100644
--- a/dencoding/yaml_decoder_test.go
+++ b/dencoding/yaml_decoder_test.go
@@ -98,12 +98,16 @@ key2: value6
 	})
 
 	t.Run("YamlAliases", func(t *testing.T) {
-		b := []byte(`foo: &foo
+		b := []byte(`foo: &foofoo
   bar: 1
-  baz: "baz"
+  baz: &baz "baz"
 spam:
   ham: "eggs"
-  <<: *foo
+  bar: 0
+  <<: *foofoo
+  baz: "bazbaz"
+
+baz: *baz
 `)
 
 		dec := dencoding.NewYAMLDecoder(bytes.NewReader(b))
@@ -121,16 +125,15 @@ spam:
 			got = append(got, v)
 		}
 
-		fooMap := dencoding.NewMap().
-			Set("bar", int64(1)).
-			Set("baz", "baz")
-		spamMap := dencoding.NewMap().
-			Set("ham", "eggs").
-			Set("foo", fooMap)
-
 		exp := dencoding.NewMap().
-			Set("foo", fooMap).
-			Set("spam", spamMap)
+			Set("foo", dencoding.NewMap().
+				Set("bar", int64(1)).
+				Set("baz", "baz")).
+			Set("spam", dencoding.NewMap().
+				Set("ham", "eggs").
+				Set("bar", int64(1)).
+				Set("baz", "bazbaz")).
+			Set("baz", "baz")
 
 		if len(got) != 1 {
 			t.Errorf("expected result len of %d, got %d", 1, len(got))
diff --git a/internal/command/select_test.go b/internal/command/select_test.go
index 31a3955..8841df7 100644
--- a/internal/command/select_test.go
+++ b/internal/command/select_test.go
@@ -254,21 +254,25 @@ octal: 8`)),
 
 	t.Run("Issue285 - YAML alias on read", runTest(
 		[]string{"-r", "yaml", "-w", "yaml"},
-		[]byte(`foo: &foo
+		[]byte(`foo: &foofoo
   bar: 1
-  baz: baz
+  baz: &baz "baz"
 spam:
-  ham: eggs
-  <<: *foo
+  ham: "eggs"
+  bar: 0
+  <<: *foofoo
+  baz: "bazbaz"
+
+baz: *baz
 `),
 		[]byte(`foo:
   bar: 1
   baz: baz
 spam:
   ham: eggs
-  foo:
-    bar: 1
-    baz: baz
+  bar: 1
+  baz: bazbaz
+baz: baz
 `),
 		nil,
 		nil,