Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CommentToMap option #253

Merged
merged 1 commit into from
Sep 7, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 30 additions & 1 deletion decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Decoder struct {
referenceReaders []io.Reader
anchorNodeMap map[string]ast.Node
anchorValueMap map[string]reflect.Value
toCommentMap CommentMap
opts []DecodeOption
referenceFiles []string
referenceDirs []string
Expand Down Expand Up @@ -115,6 +116,7 @@ func (d *Decoder) mapKeyNodeToString(node ast.Node) string {
}

func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) {
d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.MappingValueNode:
if n.Key.Type() == ast.MergeKeyType {
Expand Down Expand Up @@ -149,7 +151,30 @@ func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) {
}
}

func (d *Decoder) setPathToCommentMap(node ast.Node) {
if d.toCommentMap == nil {
return
}
commentGroup := node.GetComment()
if commentGroup == nil {
return
}
texts := []string{}
for _, comment := range commentGroup.Comments {
texts = append(texts, comment.Token.Value)
}
if len(texts) == 0 {
return
}
if len(texts) == 1 {
d.toCommentMap[node.GetPath()] = LineComment(texts[0])
} else {
d.toCommentMap[node.GetPath()] = HeadComment(texts...)
}
}

func (d *Decoder) nodeToValue(node ast.Node) interface{} {
d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.NullNode:
return nil
Expand Down Expand Up @@ -1434,7 +1459,11 @@ func (d *Decoder) resolveReference() error {
}

func (d *Decoder) parse(bytes []byte) (*ast.File, error) {
f, err := parser.ParseBytes(bytes, 0)
var parseMode parser.Mode
if d.toCommentMap != nil {
parseMode = parser.ParseComments
}
f, err := parser.ParseBytes(bytes, parseMode)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse yaml")
}
Expand Down
1 change: 1 addition & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var (
ErrInvalidPathString = xerrors.New("invalid path string")
ErrNotFoundNode = xerrors.New("node not found")
ErrUnknownCommentPositionType = xerrors.New("unknown comment position type")
ErrInvalidCommentMapValue = xerrors.New("invalid comment map value. it must be not nil value")
)

func ErrUnsupportedHeadPositionType(node ast.Node) error {
Expand Down
11 changes: 11 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,14 @@ func WithComment(cm CommentMap) EncodeOption {
return nil
}
}

// CommentToMap apply the position and content of comments in a YAML document to a CommentMap.
func CommentToMap(cm CommentMap) DecodeOption {
return func(d *Decoder) error {
if cm == nil {
return ErrInvalidCommentMapValue
}
d.toCommentMap = cm
return nil
}
}
116 changes: 116 additions & 0 deletions yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package yaml_test

import (
"bytes"
"reflect"
"testing"

"github.com/goccy/go-yaml"
Expand Down Expand Up @@ -466,3 +467,118 @@ baz:
}
})
}

func Test_CommentToMapOption(t *testing.T) {
t.Run("line comment", func(t *testing.T) {
yml := `
foo: aaa #foo comment
bar: #bar comment
bbb: ccc #bbb comment
baz:
x: 10 #x comment
`
var (
v interface{}
cm = yaml.CommentMap{}
)
if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil {
t.Fatal(err)
}
expected := []struct {
path string
comment *yaml.Comment
}{
{
path: "$.foo",
comment: yaml.LineComment("foo comment"),
},
{
path: "$.bar",
comment: yaml.LineComment("bar comment"),
},
{
path: "$.bar.bbb",
comment: yaml.LineComment("bbb comment"),
},
{
path: "$.baz.x",
comment: yaml.LineComment("x comment"),
},
}
for _, exp := range expected {
comment := cm[exp.path]
if comment == nil {
t.Fatalf("failed to get path %s", exp.path)
}
if !reflect.DeepEqual(exp.comment, comment) {
t.Fatalf("failed to get comment. expected:[%+v] but got:[%+v]", exp.comment, comment)
}
}
})
t.Run("head comment", func(t *testing.T) {
yml := `
#foo comment
#foo comment2
foo: aaa
#bar comment
#bar comment2
bar:
#bbb comment
#bbb comment2
bbb: ccc
baz:
#x comment
#x comment2
x: 10
`
var (
v interface{}
cm = yaml.CommentMap{}
)
if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil {
t.Fatal(err)
}
expected := []struct {
path string
comment *yaml.Comment
}{
{
path: "$.foo",
comment: yaml.HeadComment(
"foo comment",
"foo comment2",
),
},
{
path: "$.bar",
comment: yaml.HeadComment(
"bar comment",
"bar comment2",
),
},
{
path: "$.bar.bbb",
comment: yaml.HeadComment(
"bbb comment",
"bbb comment2",
),
},
{
path: "$.baz.x",
comment: yaml.HeadComment(
"x comment",
"x comment2",
),
},
}
for _, exp := range expected {
comment := cm[exp.path]
if comment == nil {
t.Fatalf("failed to get path %s", exp.path)
}
if !reflect.DeepEqual(exp.comment, comment) {
t.Fatalf("failed to get comment. expected:[%+v] but got:[%+v]", exp.comment, comment)
}
}
})
}