feat(marshal-goccy): marshal goccy structure

This commit is contained in:
Lars Stegman 2025-02-20 11:24:16 +01:00
parent b534aa9ee5
commit 99b8b9705b
No known key found for this signature in database
2 changed files with 195 additions and 0 deletions

View File

@ -2,6 +2,7 @@ package yqlib
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
@ -205,3 +206,154 @@ func (o *CandidateNode) goccyProcessMappingValueNode(mappingEntry *ast.MappingVa
return nil return nil
} }
func (o *CandidateNode) goccyMarshalChild(path string) (ast.Node, error) {
var node ast.Node
switch o.Kind {
case SequenceNode:
values := make([]ast.Node, len(o.Content))
for i, content := range o.Content {
childNode, err := content.goccyMarshalChild(path + "[" + strconv.Itoa(i) + "]")
if err != nil {
return nil, err
}
values[i] = childNode
}
seq := ast.Sequence(
goccyToken.SequenceStart(o.Value, &goccyToken.Position{}),
o.Style == FlowStyle,
)
seq.Values = values
seq.ValueHeadComments = []*ast.CommentGroupNode{ast.CommentGroup([]*goccyToken.Token{goccyToken.Comment(o.HeadComment, o.HeadComment, &goccyToken.Position{})})}
seq.FootComment = goccyMarshalFootComment(o.FootComment)
node = seq
case MappingNode:
values := make([]*ast.MappingValueNode, len(o.Content)/2)
for i := 0; i < len(o.Content); i += 2 {
key, err := o.Content[i].goccyMarshalMapKeyNode(path)
if err != nil {
return nil, err
}
value, err := o.Content[i+1].goccyMarshalChild(key.GetPath())
if err != nil {
return nil, err
}
childNode := ast.MappingValue(
goccyToken.MappingValue(&goccyToken.Position{}),
key,
value,
)
childNode.SetPath(key.GetPath())
if o.Content[i].FootComment != "" {
childNode.FootComment = goccyMarshalFootComment(o.Content[i].FootComment)
}
values[i/2] = childNode
}
mapping := ast.Mapping(goccyToken.MappingStart("", &goccyToken.Position{}), o.Style == FlowStyle, values...)
if o.FootComment != "" {
mapping.FootComment = goccyMarshalFootComment(o.FootComment)
}
node = mapping
case ScalarNode:
v, err := o.GetValueRep()
if err != nil {
return nil, err
}
node, err = o.goccyMarshalScalar(path, v)
if err != nil {
return nil, err
}
case AliasNode:
value, err := o.Content[0].goccyMarshalChild(path)
if err != nil {
return nil, err
}
alias := ast.Alias(goccyToken.Alias(o.Value, &goccyToken.Position{}))
alias.Value = value
node = alias
}
if o.Tag != "" && !strings.HasPrefix(o.Tag, "!!") {
tag := ast.Tag(goccyToken.Tag(o.Tag, o.Tag, &goccyToken.Position{}))
tag.Value = node
node = tag
}
if o.Anchor != "" {
anc := ast.Anchor(goccyToken.Anchor(o.Anchor, &goccyToken.Position{}))
anc.Value = node
node = anc
}
node.SetPath(path)
return node, nil
}
func (o *CandidateNode) MarshalGoccyYAML() (ast.Node, error) {
return o.goccyMarshalChild("$")
}
func goccyMarshalFootComment(comment string) *ast.CommentGroupNode {
return ast.CommentGroup([]*goccyToken.Token{goccyToken.Comment(comment, comment, &goccyToken.Position{})})
}
func (o *CandidateNode) goccyMarshalMapKeyNode(path string) (ast.MapKeyNode, error) {
if o.Kind != ScalarNode {
return nil, fmt.Errorf("cannot unmarshal non-scalar node to MapKeyNode")
}
v, err := o.GetValueRep()
if err != nil {
return nil, err
}
key, err := o.goccyMarshalScalar(path, v)
if err != nil {
return nil, err
}
key.SetPath(path + "." + key.String())
return key, nil
}
func (o *CandidateNode) goccyMarshalScalar(path string, v any) (ast.ScalarNode, error) {
t := goccyToken.Literal(o.Value, o.Value, &goccyToken.Position{})
if v == nil {
n := ast.Null(t)
n.SetPath(path)
return n, nil
}
switch av := v.(type) {
case float64:
n := ast.Float(t)
n.Value = av
return n, nil
case int64:
n := ast.Integer(t)
n.Value = av
return n, nil
case bool:
n := ast.Bool(t)
n.Value = av
return n, nil
case string:
n := ast.String(t)
n.SetPath(path)
return n, nil
}
return nil, fmt.Errorf("unknown scalar value type")
}

View File

@ -0,0 +1,43 @@
package yqlib
import (
"fmt"
"testing"
"gopkg.in/yaml.v3"
)
func TestMarshalGoccyYAML(t *testing.T) {
input := `
a:
b: 2
c: &anc
d: !mytag ef
e: 3.0
s:
- 1
- 2
`
var yamlNode yaml.Node
err := yaml.Unmarshal([]byte(input), &yamlNode)
if err != nil {
t.Error(err)
return
}
candidate := &CandidateNode{}
err = candidate.UnmarshalYAML(yamlNode.Content[0], make(map[string]*CandidateNode))
if err != nil {
t.Error(err)
return
}
goccyNode, err := candidate.MarshalGoccyYAML()
if err != nil {
t.Error(err)
return
}
fmt.Println(goccyNode)
}