Repository: goccy/go-yaml
Pull request #633 (2025-01-31 16:14:30)

Fix toString for TagNode
Fix #401 


LarsStegman: @goccy I'm not sure why the pipeline keeps failing. 
goccy: This branch must be merged with the upstream branch beforehand
codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/goccy/go-yaml/pull/633?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) Report
Attention: Patch coverage is `66.66667%` with `3 lines` in your changes missing coverage. Please review.
> Project coverage is 77.36%. Comparing base [(`c8cc5c5`)](https://app.codecov.io/gh/goccy/go-yaml/commit/c8cc5c583fe5c313b35b75f5abe8c365922b88f7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) to head [(`8f91443`)](https://app.codecov.io/gh/goccy/go-yaml/commit/8f91443e82d92f81b7dedf2c46a98525da58c028?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima).

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #633      +/-   ##
==========================================
- Coverage   77.37%   77.36%   -0.02%     
==========================================
  Files          22       22              
  Lines        7890     7899       +9     
==========================================
+ Hits         6105     6111       +6     
- Misses       1368     1370       +2     
- Partials      417      418       +1     
```

</details>
LarsStegman: Done, I thought I already did that, but apparently not

Patch:
diff --git a/ast/ast.go b/ast/ast.go
index b2a83e9..47c6dd2 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -1448,7 +1448,10 @@ func (n *MappingValueNode) toString() string {
 		return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
 	} else if _, ok := n.Value.(*AliasNode); ok {
 		return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
+	} else if _, ok := n.Value.(*TagNode); ok {
+		return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
 	}
+
 	if keyComment != nil {
 		return fmt.Sprintf(
 			"%s%s: %s\n%s",
@@ -1741,9 +1744,7 @@ func (n *AnchorNode) AddColumn(col int) {
 // String anchor to text
 func (n *AnchorNode) String() string {
 	value := n.Value.String()
-	if len(strings.Split(value, "\n")) > 1 {
-		return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
-	} else if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle {
+	if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle {
 		return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
 	} else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle {
 		return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
@@ -1922,7 +1923,14 @@ func (n *TagNode) AddColumn(col int) {
 
 // String tag to text
 func (n *TagNode) String() string {
-	return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String())
+	value := n.Value.String()
+	if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle {
+		return fmt.Sprintf("%s\n%s", n.Start.Value, value)
+	} else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle {
+		return fmt.Sprintf("%s\n%s", n.Start.Value, value)
+	}
+
+	return fmt.Sprintf("%s %s", n.Start.Value, value)
 }
 
 // MarshalYAML encodes to a YAML text
diff --git a/encode.go b/encode.go
index bb36f0f..5e0bfe3 100644
--- a/encode.go
+++ b/encode.go
@@ -622,6 +622,9 @@ func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (
 	if e.isMapNode(value) {
 		value.AddColumn(e.indentNum)
 	}
+	if e.isTagAndMapNode(value) {
+		value.AddColumn(e.indentNum)
+	}
 	return ast.MappingValue(
 		token.New("", "", e.pos(column)),
 		e.encodeString(k.Interface().(string), column),
@@ -646,6 +649,11 @@ func (e *Encoder) isMapNode(node ast.Node) bool {
 	return ok
 }
 
+func (e *Encoder) isTagAndMapNode(node ast.Node) bool {
+	tn, ok := node.(*ast.TagNode)
+	return ok && e.isMapNode(tn.Value)
+}
+
 func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node {
 	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
 	keys := make([]interface{}, len(value.MapKeys()))
@@ -665,6 +673,9 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
 		if e.isMapNode(value) {
 			value.AddColumn(e.indentNum)
 		}
+		if e.isTagAndMapNode(value) {
+			value.AddColumn(e.indentNum)
+		}
 		node.Values = append(node.Values, ast.MappingValue(
 			nil,
 			e.encodeString(fmt.Sprint(key), column),


Test patch:
diff --git a/encode_test.go b/encode_test.go
index 376964a..3095b00 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -1739,3 +1739,44 @@ values:
 		t.Fatalf("failed to encode: got = %s", string(b))
 	}
 }
+
+func TestTagMarshalling(t *testing.T) {
+	tests := []struct {
+		name  string
+		input string
+	}{
+		{name: "scalar", input: "a: !mytag 1"},
+		{name: "mapping", input: `
+a: !mytag
+  b: 2`},
+		{name: "sequence", input: `
+a: !mytag
+- 1
+- 2
+- 3`},
+		{name: "anchor before tag", input: `
+a: &anc !mytag
+- 1
+- 2
+- 3`},
+		{name: "flow mapping", input: "a: !mytag {b: 2}"},
+		{name: "flow sequence", input: "a: !mytag [1, 2, 3]"},
+		{name: "explicit type", input: "a: !!timestamp test"},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			res, _ := parser.ParseBytes([]byte(tt.input), 0)
+			result, err := yaml.Marshal(res.Docs[0])
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			expected := strings.TrimSpace(tt.input)
+			output := strings.TrimSpace(string(result))
+			if expected != output {
+				t.Fatalf("input is not equal to output.\n\nexpected:\n%v\n actual:\n%v", expected, output)
+			}
+		})
+	}
+}