+## This library has **NO** relation to the go-yaml/yaml library
+
+> [!IMPORTANT]
+> This library is developed from scratch to replace [`go-yaml/yaml`](https://github.com/go-yaml/yaml).
+> If you're looking for a better YAML library, this one should be helpful.
+
# Why a new library?
-As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However we feel that some features are lacking, namely:
+As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However, we believe that a new YAML library is necessary for the following reasons:
-- Pretty format for error notifications
-- Direct manipulation of YAML abstract syntax tree
-- Support for `Anchor` and `Alias` when marshaling
-- Allow referencing elements declared in another file via anchors
+- Not actively maintained
+- `go-yaml/yaml` has ported the libyaml written in C to Go, so the source code is not written in Go style
+- There is a lot of content that cannot be parsed
+- YAML is often used for configuration, and it is common to include validation along with it. However, the errors in `go-yaml/yaml` are not intuitive, and it is difficult to provide meaningful validation errors
+- When creating tools that use YAML, there are cases where reversible transformation of YAML is required. However, to perform reversible transformations of content that includes Comments or Anchors/Aliases, manipulating the AST is the only option
+- Non-intuitive [Marshaler](https://pkg.go.dev/gopkg.in/yaml.v3#Marshaler) / [Unmarshaler](https://pkg.go.dev/gopkg.in/yaml.v3#Unmarshaler)
+
+By the way, libraries such as [ghodss/yaml](https://github.com/ghodss/yaml) and [sigs.k8s.io/yaml](https://github.com/kubernetes-sigs/yaml) also depend on go-yaml/yaml, so if you are using these libraries, the same issues apply: they cannot parse things that go-yaml/yaml cannot parse, and they inherit many of the problems that go-yaml/yaml has.
# Features
+- No dependencies
+- A better parser than `go-yaml/yaml`.
+ - [Support recursive processing](https://github.com/apple/device-management/blob/release/docs/schema.yaml)
+ - Higher coverage in the [YAML Test Suite](https://github.com/yaml/yaml-test-suite?tab=readme-ov-file)
+ - YAML Test Suite consists of 402 cases in total, of which `gopkg.in/yaml.v3` passes `295`. In addition to passing all those test cases, `goccy/go-yaml` successfully passes nearly 60 additional test cases ( 2024/12/15 )
+ - The test code is [here](https://github.com/goccy/go-yaml/blob/master/yaml_test_suite_test.go#L77)
+- Ease and sustainability of maintenance
+ - The main maintainer is [@goccy](https://github.com/goccy), but we are also building a system to develop as a team with trusted developers
+ - Since it is written from scratch, the code is easy to read for Gophers
+- An API structure that allows the use of not only `Encoder`/`Decoder` but also `Tokenizer` and `Parser` functionalities.
+ - [lexer.Tokenize](https://pkg.go.dev/github.com/goccy/go-yaml@v1.15.4/lexer#Tokenize)
+ - [parser.Parse](https://pkg.go.dev/github.com/goccy/go-yaml@v1.15.4/parser#Parse)
+- Filtering, replacing, and merging YAML content using YAML Path
+- Reversible transformation without using the AST for YAML that includes Anchors, Aliases, and Comments
+- Customize the Marshal/Unmarshal behavior for primitive types and third-party library types ([RegisterCustomMarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#RegisterCustomMarshaler), [RegisterCustomUnmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#RegisterCustomUnmarshaler))
+- Respects `encoding/json` behavior
+ - Accept the `json` tag. Note that not all options from the `json` tag will have significance when parsing YAML documents. If both tags exist, `yaml` tag will take precedence.
+ - [json.Marshaler](https://pkg.go.dev/encoding/json#Marshaler) style [marshaler](https://pkg.go.dev/github.com/goccy/go-yaml#BytesMarshaler)
+ - [json.Unmarshaler](https://pkg.go.dev/encoding/json#Unmarshaler) style [unmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#BytesUnmarshaler)
+ - Options for using `MarshalJSON` and `UnmarshalJSON` ([UseJSONMarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#UseJSONMarshaler), [UseJSONUnmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#UseJSONUnmarshaler))
- Pretty format for error notifications
-- Supports `Scanner` or `Lexer` or `Parser` as public API
-- Supports `Anchor` and `Alias` to Marshaler
+- Smart validation processing combined with [go-playground/validator](https://github.com/go-playground/validator)
+ - [example test code is here](https://github.com/goccy/go-yaml/blob/45889c98b0a0967240eb595a1bd6896e2f575106/testdata/validate_test.go#L12)
- Allow referencing elements declared in another file via anchors
-- Extract value or AST by YAMLPath ( YAMLPath is like a JSONPath )
+
+# Users
+
+The repositories that use goccy/go-yaml are listed here.
+
+- https://github.com/goccy/go-yaml/wiki/Users
+
+The source data is [here](https://github.com/goccy/go-yaml/network/dependents).
+It is already being used in many repositories. Now it's your turn 😄
+
+# Playground
+
+The Playground visualizes how go-yaml processes YAML text. Use it to assist with your debugging or issue reporting.
+
+https://goccy.github.io/go-yaml
# Installation
```sh
-go get -u github.com/goccy/go-yaml
+go get github.com/goccy/go-yaml
```
# Synopsis
@@ -148,7 +192,9 @@ fmt.Printf("%+v\n", v) // {A:{B:1 C:hello}}
### 3.1. Explicitly declared `Anchor` name and `Alias` name
-If you want to use `anchor` or `alias`, you can define it as a struct tag.
+If you want to use `anchor`, you can define it as a struct tag.
+If the value specified for an anchor is a pointer type and the same address as the pointer is found, the value is automatically set to alias.
+If an explicit alias name is specified, an error is raised if its value is different from the value specified in the anchor.
```go
type T struct {
@@ -178,10 +224,7 @@ d: *x
If you do not explicitly declare the anchor name, the default behavior is to
use the equivalent of `strings.ToLower($FieldName)` as the name of the anchor.
-
-If you do not explicitly declare the alias name AND the value is a pointer
-to another element, we look up the anchor name by finding out which anchor
-field the value is assigned to by looking up its pointer address.
+If the value specified for an anchor is a pointer type and the same address as the pointer is found, the value is automatically set to alias.
```go
type T struct {
@@ -191,8 +234,8 @@ type T struct {
var v struct {
A *T `yaml:"a,anchor"`
B *T `yaml:"b,anchor"`
- C *T `yaml:"c,alias"`
- D *T `yaml:"d,alias"`
+ C *T `yaml:"c"`
+ D *T `yaml:"d"`
}
v.A = &T{I: 1, S: "hello"}
v.B = &T{I: 2, S: "world"}
@@ -358,9 +401,16 @@ print yaml file with color
### Installation
```sh
-go install github.com/goccy/go-yaml/cmd/ycat@latest
+git clone https://github.com/goccy/go-yaml.git
+cd go-yaml/cmd/ycat && go install .
```
+
+# For Developers
+
+> [!NOTE]
+> In this project, we manage such test code under the `testdata` directory to avoid adding dependencies on libraries that are only needed for testing to the top `go.mod` file. Therefore, if you want to add test cases that use 3rd party libraries, please add the test code to the `testdata` directory.
+
# Looking for Sponsors
I'm looking for sponsors this library. This library is being developed as a personal project in my spare time. If you want a quick response or problem resolution when using this library in your project, please register as a [sponsor](https://github.com/sponsors/goccy). I will cooperate as much as possible. Of course, this library is developed as an MIT license, so you can use it freely for free.
diff --git a/vendor/github.com/goccy/go-yaml/ast/ast.go b/vendor/github.com/goccy/go-yaml/ast/ast.go
index b4d5ec41..ca150538 100644
--- a/vendor/github.com/goccy/go-yaml/ast/ast.go
+++ b/vendor/github.com/goccy/go-yaml/ast/ast.go
@@ -1,6 +1,7 @@
package ast
import (
+ "errors"
"fmt"
"io"
"math"
@@ -8,13 +9,12 @@ import (
"strings"
"github.com/goccy/go-yaml/token"
- "golang.org/x/xerrors"
)
var (
- ErrInvalidTokenType = xerrors.New("invalid token type")
- ErrInvalidAnchorName = xerrors.New("invalid anchor name")
- ErrInvalidAliasName = xerrors.New("invalid alias name")
+ ErrInvalidTokenType = errors.New("invalid token type")
+ ErrInvalidAnchorName = errors.New("invalid anchor name")
+ ErrInvalidAliasName = errors.New("invalid alias name")
)
// NodeType type identifier of node
@@ -51,6 +51,8 @@ const (
MappingValueType
// SequenceType type identifier for sequence node
SequenceType
+ // SequenceEntryType type identifier for sequence entry node
+ SequenceEntryType
// AnchorType type identifier for anchor node
AnchorType
// AliasType type identifier for alias node
@@ -98,6 +100,8 @@ func (t NodeType) String() string {
return "MappingValue"
case SequenceType:
return "Sequence"
+ case SequenceEntryType:
+ return "SequenceEntry"
case AnchorType:
return "Anchor"
case AliasType:
@@ -148,6 +152,8 @@ func (t NodeType) YAMLName() string {
return "value"
case SequenceType:
return "sequence"
+ case SequenceEntryType:
+ return "value"
case AnchorType:
return "anchor"
case AliasType:
@@ -196,6 +202,7 @@ type Node interface {
// MapKeyNode type for map key node
type MapKeyNode interface {
Node
+ IsMergeKey() bool
// String node to text without comment
stringWithoutComment() string
}
@@ -278,6 +285,49 @@ func readNode(p []byte, node Node) (int, error) {
return size, nil
}
+func checkLineBreak(t *token.Token) bool {
+ if t.Prev != nil {
+ lbc := "\n"
+ prev := t.Prev
+ var adjustment int
+ // if the previous type is sequence entry use the previous type for that
+ if prev.Type == token.SequenceEntryType {
+ // as well as switching to previous type count any new lines in origin to account for:
+ // -
+ // b: c
+ adjustment = strings.Count(strings.TrimRight(t.Origin, lbc), lbc)
+ if prev.Prev != nil {
+ prev = prev.Prev
+ }
+ }
+ lineDiff := t.Position.Line - prev.Position.Line - 1
+ if lineDiff > 0 {
+ if prev.Type == token.StringType {
+ // Remove any line breaks included in multiline string
+ adjustment += strings.Count(strings.TrimRight(strings.TrimSpace(prev.Origin), lbc), lbc)
+ }
+ // Due to the way that comment parsing works its assumed that when a null value does not have new line in origin
+ // it was squashed therefore difference is ignored.
+ // foo:
+ // bar:
+ // # comment
+ // baz: 1
+ // becomes
+ // foo:
+ // bar: null # comment
+ //
+ // baz: 1
+ if prev.Type == token.NullType || prev.Type == token.ImplicitNullType {
+ return strings.Count(prev.Origin, lbc) > 0
+ }
+ if lineDiff-adjustment > 0 {
+ return true
+ }
+ }
+ }
+ return false
+}
+
// Null create node for null value
func Null(tk *token.Token) *NullNode {
return &NullNode{
@@ -298,105 +348,30 @@ func Bool(tk *token.Token) *BoolNode {
// Integer create node for integer value
func Integer(tk *token.Token) *IntegerNode {
- value := removeUnderScoreFromNumber(tk.Value)
- switch tk.Type {
- case token.BinaryIntegerType:
- // skip two characters because binary token starts with '0b'
- skipCharacterNum := 2
- negativePrefix := ""
- if value[0] == '-' {
- skipCharacterNum++
- negativePrefix = "-"
- }
- if len(negativePrefix) > 0 {
- i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 2, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
- }
- i, _ := strconv.ParseUint(negativePrefix+value[skipCharacterNum:], 2, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
- case token.OctetIntegerType:
- // octet token starts with '0o' or '-0o' or '0' or '-0'
- skipCharacterNum := 1
- negativePrefix := ""
- if value[0] == '-' {
- skipCharacterNum++
- if len(value) > 2 && value[2] == 'o' {
- skipCharacterNum++
- }
- negativePrefix = "-"
- } else {
- if value[1] == 'o' {
- skipCharacterNum++
- }
- }
- if len(negativePrefix) > 0 {
- i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 8, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
- }
- i, _ := strconv.ParseUint(value[skipCharacterNum:], 8, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
- case token.HexIntegerType:
- // hex token starts with '0x' or '-0x'
- skipCharacterNum := 2
- negativePrefix := ""
- if value[0] == '-' {
- skipCharacterNum++
- negativePrefix = "-"
- }
- if len(negativePrefix) > 0 {
- i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 16, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
- }
- i, _ := strconv.ParseUint(value[skipCharacterNum:], 16, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
- }
- if value[0] == '-' || value[0] == '+' {
- i, _ := strconv.ParseInt(value, 10, 64)
- return &IntegerNode{
- BaseNode: &BaseNode{},
- Token: tk,
- Value: i,
- }
+ var v any
+ if num := token.ToNumber(tk.Value); num != nil {
+ v = num.Value
}
- i, _ := strconv.ParseUint(value, 10, 64)
return &IntegerNode{
BaseNode: &BaseNode{},
Token: tk,
- Value: i,
+ Value: v,
}
}
// Float create node for float value
func Float(tk *token.Token) *FloatNode {
- f, _ := strconv.ParseFloat(removeUnderScoreFromNumber(tk.Value), 64)
+ var v float64
+ if num := token.ToNumber(tk.Value); num != nil && num.Type == token.NumberTypeFloat {
+ value, ok := num.Value.(float64)
+ if ok {
+ v = value
+ }
+ }
return &FloatNode{
BaseNode: &BaseNode{},
Token: tk,
- Value: f,
+ Value: v,
}
}
@@ -607,7 +582,9 @@ func (d *DocumentNode) String() string {
if d.Start != nil {
doc = append(doc, d.Start.Value)
}
- doc = append(doc, d.Body.String())
+ if d.Body != nil {
+ doc = append(doc, d.Body.String())
+ }
if d.End != nil {
doc = append(doc, d.End.Value)
}
@@ -619,10 +596,6 @@ func (d *DocumentNode) MarshalYAML() ([]byte, error) {
return []byte(d.String()), nil
}
-func removeUnderScoreFromNumber(num string) string {
- return strings.ReplaceAll(num, "_", "")
-}
-
// NullNode type of null node
type NullNode struct {
*BaseNode
@@ -654,6 +627,12 @@ func (n *NullNode) GetValue() interface{} {
// String returns `null` text
func (n *NullNode) String() string {
+ if n.Token.Type == token.ImplicitNullType {
+ if n.Comment != nil {
+ return n.Comment.String()
+ }
+ return ""
+ }
if n.Comment != nil {
return addCommentString("null", n.Comment)
}
@@ -669,6 +648,11 @@ func (n *NullNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *NullNode) IsMergeKey() bool {
+ return false
+}
+
// IntegerNode type of integer node
type IntegerNode struct {
*BaseNode
@@ -716,6 +700,11 @@ func (n *IntegerNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *IntegerNode) IsMergeKey() bool {
+ return false
+}
+
// FloatNode type of float node
type FloatNode struct {
*BaseNode
@@ -764,6 +753,11 @@ func (n *FloatNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *FloatNode) IsMergeKey() bool {
+ return false
+}
+
// StringNode type of string node
type StringNode struct {
*BaseNode
@@ -794,6 +788,11 @@ func (n *StringNode) GetValue() interface{} {
return n.Value
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *StringNode) IsMergeKey() bool {
+ return false
+}
+
// escapeSingleQuote escapes s to a single quoted scalar.
// https://yaml.org/spec/1.2.2/#732-single-quoted-style
func escapeSingleQuote(s string) string {
@@ -831,11 +830,12 @@ func (n *StringNode) String() string {
// It works mostly, but inconsistencies occur if line break characters are mixed.
header := token.LiteralBlockHeader(n.Value)
space := strings.Repeat(" ", n.Token.Position.Column-1)
+ indent := strings.Repeat(" ", n.Token.Position.IndentNum)
values := []string{}
for _, v := range strings.Split(n.Value, lbc) {
- values = append(values, fmt.Sprintf("%s %s", space, v))
+ values = append(values, fmt.Sprintf("%s%s%s", space, indent, v))
}
- block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
+ block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s%s%s", lbc, indent, space)), fmt.Sprintf("%s%s", indent, space))
return fmt.Sprintf("%s%s%s", header, lbc, block)
} else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') {
return fmt.Sprintf(`'%s'`, n.Value)
@@ -862,11 +862,12 @@ func (n *StringNode) stringWithoutComment() string {
// It works mostly, but inconsistencies occur if line break characters are mixed.
header := token.LiteralBlockHeader(n.Value)
space := strings.Repeat(" ", n.Token.Position.Column-1)
+ indent := strings.Repeat(" ", n.Token.Position.IndentNum)
values := []string{}
for _, v := range strings.Split(n.Value, lbc) {
- values = append(values, fmt.Sprintf("%s %s", space, v))
+ values = append(values, fmt.Sprintf("%s%s%s", space, indent, v))
}
- block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
+ block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s%s%s", lbc, indent, space)), fmt.Sprintf(" %s", space))
return fmt.Sprintf("%s%s%s", header, lbc, block)
} else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') {
return fmt.Sprintf(`'%s'`, n.Value)
@@ -931,6 +932,11 @@ func (n *LiteralNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *LiteralNode) IsMergeKey() bool {
+ return false
+}
+
// MergeKeyNode type of merge key node
type MergeKeyNode struct {
*BaseNode
@@ -974,6 +980,11 @@ func (n *MergeKeyNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *MergeKeyNode) IsMergeKey() bool {
+ return true
+}
+
// BoolNode type of boolean node
type BoolNode struct {
*BaseNode
@@ -1021,6 +1032,11 @@ func (n *BoolNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *BoolNode) IsMergeKey() bool {
+ return false
+}
+
// InfinityNode type of infinity node
type InfinityNode struct {
*BaseNode
@@ -1068,6 +1084,11 @@ func (n *InfinityNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *InfinityNode) IsMergeKey() bool {
+ return false
+}
+
// NanNode type of nan node
type NanNode struct {
*BaseNode
@@ -1114,6 +1135,11 @@ func (n *NanNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *NanNode) IsMergeKey() bool {
+ return false
+}
+
// MapNode interface of MappingValueNode / MappingNode
type MapNode interface {
MapRange() *MapNodeIter
@@ -1147,6 +1173,11 @@ func (m *MapNodeIter) Value() Node {
return m.values[m.idx].Value
}
+// KeyValue returns the MappingValueNode of the iterator's current map node entry.
+func (m *MapNodeIter) KeyValue() *MappingValueNode {
+ return m.values[m.idx]
+}
+
// MappingNode type of mapping node
type MappingNode struct {
*BaseNode
@@ -1317,13 +1348,27 @@ func (n *MappingKeyNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *MappingKeyNode) IsMergeKey() bool {
+ if n.Value == nil {
+ return false
+ }
+ key, ok := n.Value.(MapKeyNode)
+ if !ok {
+ return false
+ }
+ return key.IsMergeKey()
+}
+
// MappingValueNode type of mapping value
type MappingValueNode struct {
*BaseNode
- Start *token.Token
- Key MapKeyNode
- Value Node
- FootComment *CommentGroupNode
+ Start *token.Token // delimiter token ':'.
+ CollectEntry *token.Token // collect entry token ','.
+ Key MapKeyNode
+ Value Node
+ FootComment *CommentGroupNode
+ IsFlowStyle bool
}
// Replace replace value node.
@@ -1360,6 +1405,7 @@ func (n *MappingValueNode) AddColumn(col int) {
// SetIsFlowStyle set value to IsFlowStyle field recursively.
func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) {
+ n.IsFlowStyle = isFlow
switch value := n.Value.(type) {
case *MappingNode:
value.SetIsFlowStyle(isFlow)
@@ -1390,22 +1436,39 @@ func (n *MappingValueNode) String() string {
func (n *MappingValueNode) toString() string {
space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1)
+ if checkLineBreak(n.Key.GetToken()) {
+ space = fmt.Sprintf("%s%s", "\n", space)
+ }
keyIndentLevel := n.Key.GetToken().Position.IndentLevel
valueIndentLevel := n.Value.GetToken().Position.IndentLevel
keyComment := n.Key.GetComment()
if _, ok := n.Value.(ScalarNode); ok {
- return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
- } else if keyIndentLevel < valueIndentLevel {
+ value := n.Value.String()
+ if value == "" {
+ // implicit null value.
+ return fmt.Sprintf("%s%s:", space, n.Key.String())
+ }
+ return fmt.Sprintf("%s%s: %s", space, n.Key.String(), value)
+ } else if keyIndentLevel < valueIndentLevel && !n.IsFlowStyle {
+ valueStr := n.Value.String()
+ // For flow-style values indented on the next line, we need to add the proper indentation
+ if m, ok := n.Value.(*MappingNode); ok && m.IsFlowStyle {
+ valueIndent := strings.Repeat(" ", n.Value.GetToken().Position.Column-1)
+ valueStr = valueIndent + valueStr
+ } else if s, ok := n.Value.(*SequenceNode); ok && s.IsFlowStyle {
+ valueIndent := strings.Repeat(" ", n.Value.GetToken().Position.Column-1)
+ valueStr = valueIndent + valueStr
+ }
if keyComment != nil {
return fmt.Sprintf(
"%s%s: %s\n%s",
space,
n.Key.stringWithoutComment(),
keyComment.String(),
- n.Value.String(),
+ valueStr,
)
}
- return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String())
+ return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), valueStr)
} else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) {
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
} else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) {
@@ -1414,7 +1477,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",
@@ -1485,13 +1551,14 @@ type SequenceNode struct {
IsFlowStyle bool
Values []Node
ValueHeadComments []*CommentGroupNode
+ Entries []*SequenceEntryNode
FootComment *CommentGroupNode
}
// Replace replace value node.
func (n *SequenceNode) Replace(idx int, value Node) error {
if len(n.Values) <= idx {
- return xerrors.Errorf(
+ return fmt.Errorf(
"invalid index for sequence: sequence length is %d, but specified %d index",
len(n.Values), idx,
)
@@ -1507,6 +1574,11 @@ func (n *SequenceNode) Merge(target *SequenceNode) {
column := n.Start.Position.Column - target.Start.Position.Column
target.AddColumn(column)
n.Values = append(n.Values, target.Values...)
+ if len(target.ValueHeadComments) == 0 {
+ n.ValueHeadComments = append(n.ValueHeadComments, make([]*CommentGroupNode, len(target.Values))...)
+ return
+ }
+ n.ValueHeadComments = append(n.ValueHeadComments, target.ValueHeadComments...)
}
// SetIsFlowStyle set value to IsFlowStyle field recursively.
@@ -1562,7 +1634,15 @@ func (n *SequenceNode) blockStyleString() string {
}
for idx, value := range n.Values {
+ if value == nil {
+ continue
+ }
valueStr := value.String()
+ newLinePrefix := ""
+ if strings.HasPrefix(valueStr, "\n") {
+ valueStr = valueStr[1:]
+ newLinePrefix = "\n"
+ }
splittedValues := strings.Split(valueStr, "\n")
trimmedFirstValue := strings.TrimLeft(splittedValues[0], " ")
diffLength := len(splittedValues[0]) - len(trimmedFirstValue)
@@ -1585,9 +1665,10 @@ func (n *SequenceNode) blockStyleString() string {
}
newValue := strings.Join(newValues, "\n")
if len(n.ValueHeadComments) == len(n.Values) && n.ValueHeadComments[idx] != nil {
- values = append(values, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1))
+ values = append(values, fmt.Sprintf("%s%s", newLinePrefix, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1)))
+ newLinePrefix = ""
}
- values = append(values, fmt.Sprintf("%s- %s", space, newValue))
+ values = append(values, fmt.Sprintf("%s%s- %s", newLinePrefix, space, newValue))
}
if n.FootComment != nil {
values = append(values, n.FootComment.StringWithSpace(n.Start.Position.Column-1))
@@ -1616,6 +1697,87 @@ func (n *SequenceNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// SequenceEntryNode is the sequence entry.
+type SequenceEntryNode struct {
+ *BaseNode
+ HeadComment *CommentGroupNode // head comment.
+ LineComment *CommentGroupNode // line comment e.g.) - # comment.
+ Start *token.Token // entry token.
+ Value Node // value node.
+}
+
+// String node to text
+func (n *SequenceEntryNode) String() string {
+ return "" // TODO
+}
+
+// GetToken returns token instance
+func (n *SequenceEntryNode) GetToken() *token.Token {
+ return n.Start
+}
+
+// Type returns type of node
+func (n *SequenceEntryNode) Type() NodeType {
+ return SequenceEntryType
+}
+
+// AddColumn add column number to child nodes recursively
+func (n *SequenceEntryNode) AddColumn(col int) {
+ n.Start.AddColumn(col)
+}
+
+// SetComment set line comment.
+func (n *SequenceEntryNode) SetComment(cm *CommentGroupNode) error {
+ n.LineComment = cm
+ return nil
+}
+
+// Comment returns comment token instance
+func (n *SequenceEntryNode) GetComment() *CommentGroupNode {
+ return n.LineComment
+}
+
+// MarshalYAML
+func (n *SequenceEntryNode) MarshalYAML() ([]byte, error) {
+ return []byte(n.String()), nil
+}
+
+func (n *SequenceEntryNode) Read(p []byte) (int, error) {
+ return readNode(p, n)
+}
+
+// SequenceEntry creates SequenceEntryNode instance.
+func SequenceEntry(start *token.Token, value Node, headComment *CommentGroupNode) *SequenceEntryNode {
+ return &SequenceEntryNode{
+ BaseNode: &BaseNode{},
+ HeadComment: headComment,
+ Start: start,
+ Value: value,
+ }
+}
+
+// SequenceMergeValue creates SequenceMergeValueNode instance.
+func SequenceMergeValue(values ...MapNode) *SequenceMergeValueNode {
+ return &SequenceMergeValueNode{
+ values: values,
+ }
+}
+
+// SequenceMergeValueNode is used to convert the Sequence node specified for the merge key into a MapNode format.
+type SequenceMergeValueNode struct {
+ values []MapNode
+}
+
+// MapRange returns MapNodeIter instance.
+func (n *SequenceMergeValueNode) MapRange() *MapNodeIter {
+ ret := &MapNodeIter{idx: startRangeIndex}
+ for _, value := range n.values {
+ iter := value.MapRange()
+ ret.values = append(ret.values, iter.values...)
+ }
+ return ret
+}
+
// AnchorNode type of anchor node
type AnchorNode struct {
*BaseNode
@@ -1624,6 +1786,10 @@ type AnchorNode struct {
Value Node
}
+func (n *AnchorNode) stringWithoutComment() string {
+ return n.Value.String()
+}
+
func (n *AnchorNode) SetName(name string) error {
if n.Name == nil {
return ErrInvalidAnchorName
@@ -1649,6 +1815,10 @@ func (n *AnchorNode) GetToken() *token.Token {
return n.Start
}
+func (n *AnchorNode) GetValue() any {
+ return n.Value.GetToken().Value
+}
+
// AddColumn add column number to child nodes recursively
func (n *AnchorNode) AddColumn(col int) {
n.Start.AddColumn(col)
@@ -1662,15 +1832,18 @@ func (n *AnchorNode) AddColumn(col int) {
// String anchor to text
func (n *AnchorNode) String() string {
+ anchor := "&" + n.Name.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 {
- return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
+ if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle {
+ return fmt.Sprintf("%s\n%s", anchor, value)
} else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle {
- return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
+ return fmt.Sprintf("%s\n%s", anchor, value)
+ }
+ if value == "" {
+ // implicit null value.
+ return anchor
}
- return fmt.Sprintf("&%s %s", n.Name.String(), value)
+ return fmt.Sprintf("%s %s", anchor, value)
}
// MarshalYAML encodes to a YAML text
@@ -1678,6 +1851,18 @@ func (n *AnchorNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *AnchorNode) IsMergeKey() bool {
+ if n.Value == nil {
+ return false
+ }
+ key, ok := n.Value.(MapKeyNode)
+ if !ok {
+ return false
+ }
+ return key.IsMergeKey()
+}
+
// AliasNode type of alias node
type AliasNode struct {
*BaseNode
@@ -1685,6 +1870,10 @@ type AliasNode struct {
Value Node
}
+func (n *AliasNode) stringWithoutComment() string {
+ return n.Value.String()
+}
+
func (n *AliasNode) SetName(name string) error {
if n.Value == nil {
return ErrInvalidAliasName
@@ -1710,6 +1899,10 @@ func (n *AliasNode) GetToken() *token.Token {
return n.Start
}
+func (n *AliasNode) GetValue() any {
+ return n.Value.GetToken().Value
+}
+
// AddColumn add column number to child nodes recursively
func (n *AliasNode) AddColumn(col int) {
n.Start.AddColumn(col)
@@ -1728,11 +1921,20 @@ func (n *AliasNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *AliasNode) IsMergeKey() bool {
+ return false
+}
+
// DirectiveNode type of directive node
type DirectiveNode struct {
*BaseNode
+ // Start is '%' token.
Start *token.Token
- Value Node
+ // Name is directive name e.g.) "YAML" or "TAG".
+ Name Node
+ // Values is directive values e.g.) "1.2" or "!!" and "tag:clarkevans.com,2002:app/".
+ Values []Node
}
// Read implements (io.Reader).Read
@@ -1750,14 +1952,21 @@ func (n *DirectiveNode) GetToken() *token.Token {
// AddColumn add column number to child nodes recursively
func (n *DirectiveNode) AddColumn(col int) {
- if n.Value != nil {
- n.Value.AddColumn(col)
+ if n.Name != nil {
+ n.Name.AddColumn(col)
+ }
+ for _, value := range n.Values {
+ value.AddColumn(col)
}
}
// String directive to text
func (n *DirectiveNode) String() string {
- return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String())
+ values := make([]string, 0, len(n.Values))
+ for _, val := range n.Values {
+ values = append(values, val.String())
+ }
+ return strings.Join(append([]string{"%" + n.Name.String()}, values...), " ")
}
// MarshalYAML encodes to a YAML text
@@ -1768,8 +1977,21 @@ func (n *DirectiveNode) MarshalYAML() ([]byte, error) {
// TagNode type of tag node
type TagNode struct {
*BaseNode
- Start *token.Token
- Value Node
+ Directive *DirectiveNode
+ Start *token.Token
+ Value Node
+}
+
+func (n *TagNode) GetValue() any {
+ scalar, ok := n.Value.(ScalarNode)
+ if !ok {
+ return nil
+ }
+ return scalar.GetValue()
+}
+
+func (n *TagNode) stringWithoutComment() string {
+ return n.Value.String()
}
// Read implements (io.Reader).Read
@@ -1795,7 +2017,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
@@ -1803,6 +2032,26 @@ func (n *TagNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}
+// IsMergeKey returns whether it is a MergeKey node.
+func (n *TagNode) IsMergeKey() bool {
+ if n.Value == nil {
+ return false
+ }
+ key, ok := n.Value.(MapKeyNode)
+ if !ok {
+ return false
+ }
+ return key.IsMergeKey()
+}
+
+func (n *TagNode) ArrayRange() *ArrayNodeIter {
+ arr, ok := n.Value.(ArrayNode)
+ if !ok {
+ return nil
+ }
+ return arr.ArrayRange()
+}
+
// CommentNode type of comment node
type CommentNode struct {
*BaseNode
@@ -1880,10 +2129,13 @@ func (n *CommentGroupNode) StringWithSpace(col int) string {
values := []string{}
space := strings.Repeat(" ", col)
for _, comment := range n.Comments {
+ space := space
+ if checkLineBreak(comment.Token) {
+ space = fmt.Sprintf("%s%s", "\n", space)
+ }
values = append(values, space+comment.String())
}
return strings.Join(values, "\n")
-
}
// MarshalYAML encodes to a YAML text
@@ -1930,7 +2182,10 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Value)
case *DirectiveNode:
walkComment(v, n.BaseNode)
- Walk(v, n.Value)
+ Walk(v, n.Name)
+ for _, value := range n.Values {
+ Walk(v, value)
+ }
case *TagNode:
walkComment(v, n.BaseNode)
Walk(v, n.Value)
@@ -2016,7 +2271,14 @@ func (f *parentFinder) walk(parent, node Node) Node {
case *LiteralNode:
return f.walk(node, n.Value)
case *DirectiveNode:
- return f.walk(node, n.Value)
+ if found := f.walk(node, n.Name); found != nil {
+ return found
+ }
+ for _, value := range n.Values {
+ if found := f.walk(node, value); found != nil {
+ return found
+ }
+ }
case *TagNode:
return f.walk(node, n.Value)
case *DocumentNode:
@@ -2092,10 +2354,10 @@ func Merge(dst Node, src Node) error {
err := &ErrInvalidMergeType{dst: dst, src: src}
switch dst.Type() {
case DocumentType:
- node := dst.(*DocumentNode)
+ node, _ := dst.(*DocumentNode)
return Merge(node.Body, src)
case MappingType:
- node := dst.(*MappingNode)
+ node, _ := dst.(*MappingNode)
target, ok := src.(*MappingNode)
if !ok {
return err
@@ -2103,7 +2365,7 @@ func Merge(dst Node, src Node) error {
node.Merge(target)
return nil
case SequenceType:
- node := dst.(*SequenceNode)
+ node, _ := dst.(*SequenceNode)
target, ok := src.(*SequenceNode)
if !ok {
return err
diff --git a/vendor/github.com/goccy/go-yaml/context.go b/vendor/github.com/goccy/go-yaml/context.go
new file mode 100644
index 00000000..133f05ee
--- /dev/null
+++ b/vendor/github.com/goccy/go-yaml/context.go
@@ -0,0 +1,37 @@
+package yaml
+
+import "context"
+
+type (
+ ctxMergeKey struct{}
+ ctxAnchorKey struct{}
+)
+
+func withMerge(ctx context.Context) context.Context {
+ return context.WithValue(ctx, ctxMergeKey{}, true)
+}
+
+func isMerge(ctx context.Context) bool {
+ v, ok := ctx.Value(ctxMergeKey{}).(bool)
+ if !ok {
+ return false
+ }
+ return v
+}
+
+func withAnchor(ctx context.Context, name string) context.Context {
+ anchorMap := getAnchorMap(ctx)
+ if anchorMap == nil {
+ anchorMap = make(map[string]struct{})
+ }
+ anchorMap[name] = struct{}{}
+ return context.WithValue(ctx, ctxAnchorKey{}, anchorMap)
+}
+
+func getAnchorMap(ctx context.Context) map[string]struct{} {
+ v, ok := ctx.Value(ctxAnchorKey{}).(map[string]struct{})
+ if !ok {
+ return nil
+ }
+ return v
+}
diff --git a/vendor/github.com/goccy/go-yaml/decode.go b/vendor/github.com/goccy/go-yaml/decode.go
index 72af5e22..43c317f8 100644
--- a/vendor/github.com/goccy/go-yaml/decode.go
+++ b/vendor/github.com/goccy/go-yaml/decode.go
@@ -7,18 +7,19 @@ import (
"encoding/base64"
"fmt"
"io"
+ "maps"
"math"
"os"
"path/filepath"
"reflect"
"sort"
"strconv"
+ "strings"
"time"
- "golang.org/x/xerrors"
-
"github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/internal/errors"
+ "github.com/goccy/go-yaml/internal/format"
"github.com/goccy/go-yaml/parser"
"github.com/goccy/go-yaml/token"
)
@@ -29,7 +30,8 @@ type Decoder struct {
referenceReaders []io.Reader
anchorNodeMap map[string]ast.Node
anchorValueMap map[string]reflect.Value
- customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error
+ customUnmarshalerMap map[reflect.Type]func(context.Context, interface{}, []byte) error
+ commentMaps []CommentMap
toCommentMap CommentMap
opts []DecodeOption
referenceFiles []string
@@ -38,11 +40,13 @@ type Decoder struct {
isResolvedReference bool
validator StructValidator
disallowUnknownField bool
- disallowDuplicateKey bool
+ allowedFieldPrefixes []string
+ allowDuplicateMapKey bool
useOrderedMap bool
useJSONUnmarshaler bool
parsedFile *ast.File
streamIndex int
+ decodeDepth int
}
// NewDecoder returns a new decoder that reads from r.
@@ -51,7 +55,7 @@ func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder {
reader: r,
anchorNodeMap: map[string]ast.Node{},
anchorValueMap: map[string]reflect.Value{},
- customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{},
+ customUnmarshalerMap: map[reflect.Type]func(context.Context, interface{}, []byte) error{},
opts: opts,
referenceReaders: []io.Reader{},
referenceFiles: []string{},
@@ -59,11 +63,25 @@ func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder {
isRecursiveDir: false,
isResolvedReference: false,
disallowUnknownField: false,
- disallowDuplicateKey: false,
+ allowDuplicateMapKey: false,
useOrderedMap: false,
}
}
+const maxDecodeDepth = 10000
+
+func (d *Decoder) stepIn() {
+ d.decodeDepth++
+}
+
+func (d *Decoder) stepOut() {
+ d.decodeDepth--
+}
+
+func (d *Decoder) isExceededMaxDepth() bool {
+ return d.decodeDepth > maxDecodeDepth
+}
+
func (d *Decoder) castToFloat(v interface{}) interface{} {
switch vv := v.(type) {
case int:
@@ -98,63 +116,111 @@ func (d *Decoder) castToFloat(v interface{}) interface{} {
return 0
}
-func (d *Decoder) mergeValueNode(value ast.Node) ast.Node {
- if value.Type() == ast.AliasType {
- aliasNode := value.(*ast.AliasNode)
- aliasName := aliasNode.Value.GetToken().Value
- return d.anchorNodeMap[aliasName]
+func (d *Decoder) mapKeyNodeToString(ctx context.Context, node ast.MapKeyNode) (string, error) {
+ key, err := d.nodeToValue(ctx, node)
+ if err != nil {
+ return "", err
}
- return value
-}
-
-func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) string {
- key := d.nodeToValue(node)
if key == nil {
- return "null"
+ return "null", nil
}
if k, ok := key.(string); ok {
- return k
+ return k, nil
}
- return fmt.Sprint(key)
+ return fmt.Sprint(key), nil
}
-func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) {
+func (d *Decoder) setToMapValue(ctx context.Context, node ast.Node, m map[string]interface{}) error {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
+ }
+
d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.MappingValueNode:
- if n.Key.Type() == ast.MergeKeyType {
- d.setToMapValue(d.mergeValueNode(n.Value), m)
+ if n.Key.IsMergeKey() {
+ value, err := d.getMapNode(n.Value, true)
+ if err != nil {
+ return err
+ }
+ iter := value.MapRange()
+ for iter.Next() {
+ if err := d.setToMapValue(ctx, iter.KeyValue(), m); err != nil {
+ return err
+ }
+ }
} else {
- key := d.mapKeyNodeToString(n.Key)
- m[key] = d.nodeToValue(n.Value)
+ key, err := d.mapKeyNodeToString(ctx, n.Key)
+ if err != nil {
+ return err
+ }
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return err
+ }
+ m[key] = v
}
case *ast.MappingNode:
for _, value := range n.Values {
- d.setToMapValue(value, m)
+ if err := d.setToMapValue(ctx, value, m); err != nil {
+ return err
+ }
}
case *ast.AnchorNode:
anchorName := n.Name.GetToken().Value
d.anchorNodeMap[anchorName] = n.Value
}
+ return nil
}
-func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) {
+func (d *Decoder) setToOrderedMapValue(ctx context.Context, node ast.Node, m *MapSlice) error {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
+ }
+
+ d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.MappingValueNode:
- if n.Key.Type() == ast.MergeKeyType {
- d.setToOrderedMapValue(d.mergeValueNode(n.Value), m)
+ if n.Key.IsMergeKey() {
+ value, err := d.getMapNode(n.Value, true)
+ if err != nil {
+ return err
+ }
+ iter := value.MapRange()
+ for iter.Next() {
+ if err := d.setToOrderedMapValue(ctx, iter.KeyValue(), m); err != nil {
+ return err
+ }
+ }
} else {
- key := d.mapKeyNodeToString(n.Key)
- *m = append(*m, MapItem{Key: key, Value: d.nodeToValue(n.Value)})
+ key, err := d.mapKeyNodeToString(ctx, n.Key)
+ if err != nil {
+ return err
+ }
+ value, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return err
+ }
+ *m = append(*m, MapItem{Key: key, Value: value})
}
case *ast.MappingNode:
for _, value := range n.Values {
- d.setToOrderedMapValue(value, m)
+ if err := d.setToOrderedMapValue(ctx, value, m); err != nil {
+ return err
+ }
}
}
+ return nil
}
func (d *Decoder) setPathToCommentMap(node ast.Node) {
+ if node == nil {
+ return
+ }
if d.toCommentMap == nil {
return
}
@@ -186,6 +252,14 @@ func (d *Decoder) addHeadOrLineCommentToMap(node ast.Node) {
}
commentPath := node.GetPath()
if minCommentLine < targetLine {
+ switch n := node.(type) {
+ case *ast.MappingNode:
+ if len(n.Values) != 0 {
+ commentPath = n.Values[0].Key.GetPath()
+ }
+ case *ast.MappingValueNode:
+ commentPath = n.Key.GetPath()
+ }
d.addCommentToMap(commentPath, HeadComment(texts...))
} else {
d.addCommentToMap(commentPath, LineComment(texts[0]))
@@ -222,18 +296,24 @@ func (d *Decoder) addSequenceNodeCommentToMap(node *ast.SequenceNode) {
func (d *Decoder) addFootCommentToMap(node ast.Node) {
var (
footComment *ast.CommentGroupNode
- footCommentPath string = node.GetPath()
+ footCommentPath = node.GetPath()
)
switch n := node.(type) {
case *ast.SequenceNode:
- if len(n.Values) != 0 {
- footCommentPath = n.Values[len(n.Values)-1].GetPath()
- }
footComment = n.FootComment
+ if n.FootComment != nil {
+ footCommentPath = n.FootComment.GetPath()
+ }
case *ast.MappingNode:
footComment = n.FootComment
+ if n.FootComment != nil {
+ footCommentPath = n.FootComment.GetPath()
+ }
case *ast.MappingValueNode:
footComment = n.FootComment
+ if n.FootComment != nil {
+ footCommentPath = n.FootComment.GetPath()
+ }
}
if footComment == nil {
return
@@ -260,192 +340,251 @@ func (d *Decoder) addCommentToMap(path string, comment *Comment) {
})
}
-func (d *Decoder) nodeToValue(node ast.Node) interface{} {
+func (d *Decoder) nodeToValue(ctx context.Context, node ast.Node) (any, error) {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return nil, ErrExceededMaxDepth
+ }
+
d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.NullNode:
- return nil
+ return nil, nil
case *ast.StringNode:
- return n.GetValue()
+ return n.GetValue(), nil
case *ast.IntegerNode:
- return n.GetValue()
+ return n.GetValue(), nil
case *ast.FloatNode:
- return n.GetValue()
+ return n.GetValue(), nil
case *ast.BoolNode:
- return n.GetValue()
+ return n.GetValue(), nil
case *ast.InfinityNode:
- return n.GetValue()
+ return n.GetValue(), nil
case *ast.NanNode:
- return n.GetValue()
+ return n.GetValue(), nil
case *ast.TagNode:
+ if n.Directive != nil {
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ if v == nil {
+ return "", nil
+ }
+ return fmt.Sprint(v), nil
+ }
switch token.ReservedTagKeyword(n.Start.Value) {
case token.TimestampTag:
- t, _ := d.castToTime(n.Value)
- return t
+ t, _ := d.castToTime(ctx, n.Value)
+ return t, nil
case token.IntegerTag:
- i, _ := strconv.Atoi(fmt.Sprint(d.nodeToValue(n.Value)))
- return i
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ i, _ := strconv.Atoi(fmt.Sprint(v))
+ return i, nil
case token.FloatTag:
- return d.castToFloat(d.nodeToValue(n.Value))
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ return d.castToFloat(v), nil
case token.NullTag:
- return nil
+ return nil, nil
case token.BinaryTag:
- b, _ := base64.StdEncoding.DecodeString(d.nodeToValue(n.Value).(string))
- return b
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ str, ok := v.(string)
+ if !ok {
+ return nil, errors.ErrSyntax(
+ fmt.Sprintf("cannot convert %q to string", fmt.Sprint(v)),
+ n.Value.GetToken(),
+ )
+ }
+ b, _ := base64.StdEncoding.DecodeString(str)
+ return b, nil
+ case token.BooleanTag:
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ str := strings.ToLower(fmt.Sprint(v))
+ b, err := strconv.ParseBool(str)
+ if err == nil {
+ return b, nil
+ }
+ switch str {
+ case "yes":
+ return true, nil
+ case "no":
+ return false, nil
+ }
+ return nil, errors.ErrSyntax(fmt.Sprintf("cannot convert %q to boolean", fmt.Sprint(v)), n.Value.GetToken())
case token.StringTag:
- return d.nodeToValue(n.Value)
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ if v == nil {
+ return "", nil
+ }
+ return fmt.Sprint(v), nil
case token.MappingTag:
- return d.nodeToValue(n.Value)
+ return d.nodeToValue(ctx, n.Value)
+ default:
+ return d.nodeToValue(ctx, n.Value)
}
case *ast.AnchorNode:
anchorName := n.Name.GetToken().Value
- anchorValue := d.nodeToValue(n.Value)
+
+ // To handle the case where alias is processed recursively, the result of alias can be set to nil in advance.
+ d.anchorNodeMap[anchorName] = nil
+ anchorValue, err := d.nodeToValue(withAnchor(ctx, anchorName), n.Value)
+ if err != nil {
+ delete(d.anchorNodeMap, anchorName)
+ return nil, err
+ }
d.anchorNodeMap[anchorName] = n.Value
- return anchorValue
+ d.anchorValueMap[anchorName] = reflect.ValueOf(anchorValue)
+ return anchorValue, nil
case *ast.AliasNode:
+ text := n.Value.String()
+ if _, exists := getAnchorMap(ctx)[text]; exists {
+ // self recursion.
+ return nil, nil
+ }
+ if v, exists := d.anchorValueMap[text]; exists {
+ if !v.IsValid() {
+ return nil, nil
+ }
+ return v.Interface(), nil
+ }
aliasName := n.Value.GetToken().Value
- node := d.anchorNodeMap[aliasName]
- return d.nodeToValue(node)
+ return nil, errors.ErrSyntax(fmt.Sprintf("could not find alias %q", aliasName), n.Value.GetToken())
case *ast.LiteralNode:
- return n.Value.GetValue()
+ return n.Value.GetValue(), nil
case *ast.MappingKeyNode:
- return d.nodeToValue(n.Value)
+ return d.nodeToValue(ctx, n.Value)
case *ast.MappingValueNode:
- if n.Key.Type() == ast.MergeKeyType {
- value := d.mergeValueNode(n.Value)
+ if n.Key.IsMergeKey() {
+ value, err := d.getMapNode(n.Value, true)
+ if err != nil {
+ return nil, err
+ }
+ iter := value.MapRange()
if d.useOrderedMap {
m := MapSlice{}
- d.setToOrderedMapValue(value, &m)
- return m
+ for iter.Next() {
+ if err := d.setToOrderedMapValue(ctx, iter.KeyValue(), &m); err != nil {
+ return nil, err
+ }
+ }
+ return m, nil
+ }
+ m := make(map[string]any)
+ for iter.Next() {
+ if err := d.setToMapValue(ctx, iter.KeyValue(), m); err != nil {
+ return nil, err
+ }
}
- m := map[string]interface{}{}
- d.setToMapValue(value, m)
- return m
+ return m, nil
+ }
+ key, err := d.mapKeyNodeToString(ctx, n.Key)
+ if err != nil {
+ return nil, err
}
- key := d.mapKeyNodeToString(n.Key)
if d.useOrderedMap {
- return MapSlice{{Key: key, Value: d.nodeToValue(n.Value)}}
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
+ }
+ return MapSlice{{Key: key, Value: v}}, nil
}
- return map[string]interface{}{
- key: d.nodeToValue(n.Value),
+ v, err := d.nodeToValue(ctx, n.Value)
+ if err != nil {
+ return nil, err
}
+ return map[string]interface{}{key: v}, nil
case *ast.MappingNode:
if d.useOrderedMap {
m := make(MapSlice, 0, len(n.Values))
for _, value := range n.Values {
- d.setToOrderedMapValue(value, &m)
+ if err := d.setToOrderedMapValue(ctx, value, &m); err != nil {
+ return nil, err
+ }
}
- return m
+ return m, nil
}
m := make(map[string]interface{}, len(n.Values))
for _, value := range n.Values {
- d.setToMapValue(value, m)
- }
- return m
- case *ast.SequenceNode:
- v := make([]interface{}, 0, len(n.Values))
- for _, value := range n.Values {
- v = append(v, d.nodeToValue(value))
- }
- return v
- }
- return nil
-}
-
-func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) {
- switch n := node.(type) {
- case *ast.MappingNode:
- for idx, v := range n.Values {
- value, err := d.resolveAlias(v)
- if err != nil {
- return nil, err
- }
- n.Values[idx] = value.(*ast.MappingValueNode)
- }
- case *ast.TagNode:
- value, err := d.resolveAlias(n.Value)
- if err != nil {
- return nil, err
- }
- n.Value = value
- case *ast.MappingKeyNode:
- value, err := d.resolveAlias(n.Value)
- if err != nil {
- return nil, err
- }
- n.Value = value
- case *ast.MappingValueNode:
- if n.Key.Type() == ast.MergeKeyType && n.Value.Type() == ast.AliasType {
- value, err := d.resolveAlias(n.Value)
- if err != nil {
- return nil, err
- }
- keyColumn := n.Key.GetToken().Position.Column
- requiredColumn := keyColumn + 2
- value.AddColumn(requiredColumn)
- n.Value = value
- } else {
- key, err := d.resolveAlias(n.Key)
- if err != nil {
+ if err := d.setToMapValue(ctx, value, m); err != nil {
return nil, err
}
- n.Key = key.(ast.MapKeyNode)
- value, err := d.resolveAlias(n.Value)
- if err != nil {
- return nil, err
- }
- n.Value = value
}
+ return m, nil
case *ast.SequenceNode:
- for idx, v := range n.Values {
- value, err := d.resolveAlias(v)
+ v := make([]interface{}, 0, len(n.Values))
+ for _, value := range n.Values {
+ vv, err := d.nodeToValue(ctx, value)
if err != nil {
return nil, err
}
- n.Values[idx] = value
- }
- case *ast.AliasNode:
- aliasName := n.Value.GetToken().Value
- node := d.anchorNodeMap[aliasName]
- if node == nil {
- return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName)
+ v = append(v, vv)
}
- return d.resolveAlias(node)
+ return v, nil
}
- return node, nil
+ return nil, nil
}
-func (d *Decoder) getMapNode(node ast.Node) (ast.MapNode, error) {
- if _, ok := node.(*ast.NullNode); ok {
- return nil, nil
+func (d *Decoder) getMapNode(node ast.Node, isMerge bool) (ast.MapNode, error) {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return nil, ErrExceededMaxDepth
}
- if anchor, ok := node.(*ast.AnchorNode); ok {
- mapNode, ok := anchor.Value.(ast.MapNode)
- if ok {
- return mapNode, nil
- }
- return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.MappingType, node.GetToken())
- }
- if alias, ok := node.(*ast.AliasNode); ok {
- aliasName := alias.Value.GetToken().Value
+
+ switch n := node.(type) {
+ case ast.MapNode:
+ return n, nil
+ case *ast.AnchorNode:
+ anchorName := n.Name.GetToken().Value
+ d.anchorNodeMap[anchorName] = n.Value
+ return d.getMapNode(n.Value, isMerge)
+ case *ast.AliasNode:
+ aliasName := n.Value.GetToken().Value
node := d.anchorNodeMap[aliasName]
if node == nil {
- return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName)
+ return nil, fmt.Errorf("cannot find anchor by alias name %s", aliasName)
}
- mapNode, ok := node.(ast.MapNode)
- if ok {
- return mapNode, nil
+ return d.getMapNode(node, isMerge)
+ case *ast.SequenceNode:
+ if !isMerge {
+ return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken())
}
- return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken())
- }
- mapNode, ok := node.(ast.MapNode)
- if !ok {
- return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken())
+ var mapNodes []ast.MapNode
+ for _, value := range n.Values {
+ mapNode, err := d.getMapNode(value, false)
+ if err != nil {
+ return nil, err
+ }
+ mapNodes = append(mapNodes, mapNode)
+ }
+ return ast.SequenceMergeValue(mapNodes...), nil
}
- return mapNode, nil
+ return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken())
}
func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return nil, ErrExceededMaxDepth
+ }
+
if _, ok := node.(*ast.NullNode); ok {
return nil, nil
}
@@ -455,36 +594,27 @@ func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) {
return arrayNode, nil
}
- return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken())
+ return nil, errors.ErrUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken())
}
if alias, ok := node.(*ast.AliasNode); ok {
aliasName := alias.Value.GetToken().Value
node := d.anchorNodeMap[aliasName]
if node == nil {
- return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName)
+ return nil, fmt.Errorf("cannot find anchor by alias name %s", aliasName)
}
arrayNode, ok := node.(ast.ArrayNode)
if ok {
return arrayNode, nil
}
- return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken())
+ return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken())
}
arrayNode, ok := node.(ast.ArrayNode)
if !ok {
- return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken())
+ return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken())
}
return arrayNode, nil
}
-func (d *Decoder) fileToNode(f *ast.File) ast.Node {
- for _, doc := range f.Docs {
- if v := d.nodeToValue(doc.Body); v != nil {
- return doc.Body
- }
- }
- return nil
-}
-
func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) {
if typ.Kind() != reflect.String {
if !v.Type().ConvertibleTo(typ) {
@@ -503,70 +633,34 @@ func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node)
// else, fall through to the error below
}
}
- return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken())
+ return reflect.Zero(typ), errors.ErrTypeMismatch(typ, v.Type(), src.GetToken())
}
return v.Convert(typ), nil
}
// cast value to string
+ var strVal string
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return reflect.ValueOf(fmt.Sprint(v.Int())), nil
+ strVal = strconv.FormatInt(v.Int(), 10)
case reflect.Float32, reflect.Float64:
- return reflect.ValueOf(fmt.Sprint(v.Float())), nil
+ strVal = fmt.Sprint(v.Float())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return reflect.ValueOf(fmt.Sprint(v.Uint())), nil
+ strVal = strconv.FormatUint(v.Uint(), 10)
case reflect.Bool:
- return reflect.ValueOf(fmt.Sprint(v.Bool())), nil
- }
- if !v.Type().ConvertibleTo(typ) {
- return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken())
+ strVal = strconv.FormatBool(v.Bool())
+ default:
+ if !v.Type().ConvertibleTo(typ) {
+ return reflect.Zero(typ), errors.ErrTypeMismatch(typ, v.Type(), src.GetToken())
+ }
+ return v.Convert(typ), nil
}
- return v.Convert(typ), nil
-}
-
-type overflowError struct {
- dstType reflect.Type
- srcNum string
-}
-
-func (e *overflowError) Error() string {
- return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType)
-}
-
-func errOverflow(dstType reflect.Type, num string) *overflowError {
- return &overflowError{dstType: dstType, srcNum: num}
-}
-
-func errTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *errors.TypeError {
- return &errors.TypeError{DstType: dstType, SrcType: srcType, Token: token}
-}
-
-type unknownFieldError struct {
- err error
-}
-
-func (e *unknownFieldError) Error() string {
- return e.err.Error()
-}
-
-func errUnknownField(msg string, tk *token.Token) *unknownFieldError {
- return &unknownFieldError{err: errors.ErrSyntax(msg, tk)}
-}
-func errUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) error {
- return errors.ErrSyntax(fmt.Sprintf("%s was used where %s is expected", actual.YAMLName(), expected.YAMLName()), tk)
-}
-
-type duplicateKeyError struct {
- err error
-}
-
-func (e *duplicateKeyError) Error() string {
- return e.err.Error()
-}
-
-func errDuplicateKey(msg string, tk *token.Token) *duplicateKeyError {
- return &duplicateKeyError{err: errors.ErrSyntax(msg, tk)}
+ val := reflect.ValueOf(strVal)
+ if val.Type() != typ {
+ // Handle named types, e.g., `type MyString string`
+ val = val.Convert(typ)
+ }
+ return val, nil
}
func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[string]ast.Node) error {
@@ -575,7 +669,7 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st
}
structFieldMap, err := structFieldMap(structType)
if err != nil {
- return errors.Wrapf(err, "failed to create struct field map")
+ return err
}
for j := 0; j < structType.NumField(); j++ {
@@ -590,7 +684,7 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st
}
if structField.IsInline {
- d.deleteStructKeys(field.Type, unknownFields)
+ _ = d.deleteStructKeys(field.Type, unknownFields)
} else {
delete(unknownFields, structField.RenderName)
}
@@ -598,57 +692,18 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st
return nil
}
-func (d *Decoder) lastNode(node ast.Node) ast.Node {
- switch n := node.(type) {
- case *ast.MappingNode:
- if len(n.Values) > 0 {
- return d.lastNode(n.Values[len(n.Values)-1])
- }
- case *ast.MappingValueNode:
- return d.lastNode(n.Value)
- case *ast.SequenceNode:
- if len(n.Values) > 0 {
- return d.lastNode(n.Values[len(n.Values)-1])
- }
- }
- return node
-}
-
func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) {
- var err error
- node, err = d.resolveAlias(node)
- if err != nil {
- return nil, err
- }
- doc := node.String()
- last := d.lastNode(node)
- if last != nil && last.Type() == ast.LiteralType {
- doc += "\n"
- }
+ doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap)
return []byte(doc), nil
}
-func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) {
- var err error
- node, err = d.resolveAlias(node)
- if err != nil {
- return nil, false, err
- }
- if node.Type() == ast.AnchorType {
- node = node.(*ast.AnchorNode).Value
- }
- switch n := node.(type) {
- case *ast.StringNode:
- return []byte(n.Value), true, nil
- case *ast.LiteralNode:
- return []byte(n.Value.GetToken().Value), true, nil
- default:
- scalar, ok := n.(ast.ScalarNode)
- if ok {
- return []byte(fmt.Sprint(scalar.GetValue())), true, nil
- }
+func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool) {
+ doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap)
+ var v string
+ if err := Unmarshal([]byte(doc), &v); err != nil {
+ return nil, false
}
- return nil, false, nil
+ return []byte(v), true
}
type jsonUnmarshaler interface {
@@ -668,7 +723,7 @@ func (d *Decoder) existsTypeInCustomUnmarshalerMap(t reflect.Type) bool {
return false
}
-func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(interface{}, []byte) error, bool) {
+func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(context.Context, interface{}, []byte) error, bool) {
if unmarshaler, exists := d.customUnmarshalerMap[t]; exists {
return unmarshaler, exists
}
@@ -688,19 +743,15 @@ func (d *Decoder) canDecodeByUnmarshaler(dst reflect.Value) bool {
}
iface := ptrValue.Interface()
switch iface.(type) {
- case BytesUnmarshalerContext:
- return true
- case BytesUnmarshaler:
- return true
- case InterfaceUnmarshalerContext:
- return true
- case InterfaceUnmarshaler:
- return true
- case *time.Time:
- return true
- case *time.Duration:
- return true
- case encoding.TextUnmarshaler:
+ case BytesUnmarshalerContext,
+ BytesUnmarshaler,
+ InterfaceUnmarshalerContext,
+ InterfaceUnmarshaler,
+ NodeUnmarshaler,
+ NodeUnmarshalerContext,
+ *time.Time,
+ *time.Duration,
+ encoding.TextUnmarshaler:
return true
case jsonUnmarshaler:
return d.useJSONUnmarshaler
@@ -713,10 +764,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
if unmarshaler, exists := d.unmarshalerFromCustomUnmarshalerMap(ptrValue.Type()); exists {
b, err := d.unmarshalableDocument(src)
if err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
}
- if err := unmarshaler(ptrValue.Interface(), b); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ if err := unmarshaler(ctx, ptrValue.Interface(), b); err != nil {
+ return err
}
return nil
}
@@ -725,10 +776,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
if unmarshaler, ok := iface.(BytesUnmarshalerContext); ok {
b, err := d.unmarshalableDocument(src)
if err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
}
if err := unmarshaler.UnmarshalYAML(ctx, b); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
}
return nil
}
@@ -736,10 +787,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
if unmarshaler, ok := iface.(BytesUnmarshaler); ok {
b, err := d.unmarshalableDocument(src)
if err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
}
if err := unmarshaler.UnmarshalYAML(b); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
}
return nil
}
@@ -748,14 +799,14 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
if err := unmarshaler.UnmarshalYAML(ctx, func(v interface{}) error {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Ptr {
- return errors.ErrDecodeRequiredPointerType
+ return ErrDecodeRequiredPointerType
}
if err := d.decodeValue(ctx, rv.Elem(), src); err != nil {
- return errors.Wrapf(err, "failed to decode value")
+ return err
}
return nil
}); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
}
return nil
}
@@ -764,15 +815,31 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
if err := unmarshaler.UnmarshalYAML(func(v interface{}) error {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Ptr {
- return errors.ErrDecodeRequiredPointerType
+ return ErrDecodeRequiredPointerType
}
if err := d.decodeValue(ctx, rv.Elem(), src); err != nil {
- return errors.Wrapf(err, "failed to decode value")
+ return err
}
return nil
}); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalYAML")
+ return err
+ }
+ return nil
+ }
+
+ if unmarshaler, ok := iface.(NodeUnmarshaler); ok {
+ if err := unmarshaler.UnmarshalYAML(src); err != nil {
+ return err
}
+
+ return nil
+ }
+
+ if unmarshaler, ok := iface.(NodeUnmarshalerContext); ok {
+ if err := unmarshaler.UnmarshalYAML(ctx, src); err != nil {
+ return err
+ }
+
return nil
}
@@ -785,13 +852,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
}
if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText {
- b, ok, err := d.unmarshalableText(src)
- if err != nil {
- return errors.Wrapf(err, "failed to UnmarshalText")
- }
+ b, ok := d.unmarshalableText(src)
if ok {
if err := unmarshaler.UnmarshalText(b); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalText")
+ return err
}
return nil
}
@@ -801,21 +865,21 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
if unmarshaler, ok := iface.(jsonUnmarshaler); ok {
b, err := d.unmarshalableDocument(src)
if err != nil {
- return errors.Wrapf(err, "failed to UnmarshalJSON")
+ return err
}
jsonBytes, err := YAMLToJSON(b)
if err != nil {
- return errors.Wrapf(err, "failed to convert yaml to json")
+ return err
}
jsonBytes = bytes.TrimRight(jsonBytes, "\n")
if err := unmarshaler.UnmarshalJSON(jsonBytes); err != nil {
- return errors.Wrapf(err, "failed to UnmarshalJSON")
+ return err
}
return nil
}
}
- return xerrors.Errorf("does not implemented Unmarshaler")
+ return errors.New("does not implemented Unmarshaler")
}
var (
@@ -823,15 +887,27 @@ var (
)
func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.Node) error {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
+ }
+ if !dst.IsValid() {
+ return nil
+ }
+
if src.Type() == ast.AnchorType {
- anchorName := src.(*ast.AnchorNode).Name.GetToken().Value
- if _, exists := d.anchorValueMap[anchorName]; !exists {
- d.anchorValueMap[anchorName] = dst
+ anchor, _ := src.(*ast.AnchorNode)
+ anchorName := anchor.Name.GetToken().Value
+ if err := d.decodeValue(withAnchor(ctx, anchorName), dst, anchor.Value); err != nil {
+ return err
}
+ d.anchorValueMap[anchorName] = dst
+ return nil
}
if d.canDecodeByUnmarshaler(dst) {
if err := d.decodeByUnmarshaler(ctx, dst, src); err != nil {
- return errors.Wrapf(err, "failed to decode by unmarshaler")
+ return err
}
return nil
}
@@ -848,17 +924,27 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
}
v := d.createDecodableValue(dst.Type())
if err := d.decodeValue(ctx, v, src); err != nil {
- return errors.Wrapf(err, "failed to decode ptr value")
+ return err
+ }
+ castedValue, err := d.castToAssignableValue(v, dst.Type(), src)
+ if err != nil {
+ return err
}
- dst.Set(d.castToAssignableValue(v, dst.Type()))
+ dst.Set(castedValue)
case reflect.Interface:
if dst.Type() == astNodeType {
dst.Set(reflect.ValueOf(src))
return nil
}
- v := reflect.ValueOf(d.nodeToValue(src))
+ srcVal, err := d.nodeToValue(ctx, src)
+ if err != nil {
+ return err
+ }
+ v := reflect.ValueOf(srcVal)
if v.IsValid() {
dst.Set(v)
+ } else {
+ dst.Set(reflect.Zero(valueType))
}
case reflect.Map:
return d.decodeMap(ctx, dst, src)
@@ -875,7 +961,10 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
}
return d.decodeStruct(ctx, dst, src)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- v := d.nodeToValue(src)
+ v, err := d.nodeToValue(ctx, src)
+ if err != nil {
+ return err
+ }
switch vv := v.(type) {
case int64:
if !dst.OverflowInt(vv) {
@@ -899,14 +988,17 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
return nil
}
} else { // couldn't be parsed as float
- return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
+ return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
- return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
+ return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
- return errOverflow(valueType, fmt.Sprint(v))
+ return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- v := d.nodeToValue(src)
+ v, err := d.nodeToValue(ctx, src)
+ if err != nil {
+ return err
+ }
switch vv := v.(type) {
case int64:
if 0 <= vv && !dst.OverflowUint(uint64(vv)) {
@@ -930,19 +1022,23 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
return nil
}
} else { // couldn't be parsed as float
- return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
+ return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
- return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
+ return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
- return errOverflow(valueType, fmt.Sprint(v))
+ return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken())
}
- v := reflect.ValueOf(d.nodeToValue(src))
+ srcVal, err := d.nodeToValue(ctx, src)
+ if err != nil {
+ return err
+ }
+ v := reflect.ValueOf(srcVal)
if v.IsValid() {
convertedValue, err := d.convertValue(v, dst.Type(), src)
if err != nil {
- return errors.Wrapf(err, "failed to convert value")
+ return err
}
dst.Set(convertedValue)
}
@@ -960,23 +1056,29 @@ func (d *Decoder) createDecodableValue(typ reflect.Type) reflect.Value {
return reflect.New(typ).Elem()
}
-func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type) reflect.Value {
+func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type, src ast.Node) (reflect.Value, error) {
if target.Kind() != reflect.Ptr {
- return value
- }
- maxTryCount := 5
- tryCount := 0
- for {
- if tryCount > maxTryCount {
- return value
+ if !value.Type().AssignableTo(target) {
+ return reflect.Value{}, errors.ErrTypeMismatch(target, value.Type(), src.GetToken())
}
+ return value, nil
+ }
+
+ const maxAddrCount = 5
+
+ for i := 0; i < maxAddrCount; i++ {
if value.Type().AssignableTo(target) {
break
}
+ if !value.CanAddr() {
+ break
+ }
value = value.Addr()
- tryCount++
}
- return value
+ if !value.Type().AssignableTo(target) {
+ return reflect.Value{}, errors.ErrTypeMismatch(target, value.Type(), src.GetToken())
+ }
+ return value, nil
}
func (d *Decoder) createDecodedNewValue(
@@ -984,61 +1086,79 @@ func (d *Decoder) createDecodedNewValue(
) (reflect.Value, error) {
if node.Type() == ast.AliasType {
aliasName := node.(*ast.AliasNode).Value.GetToken().Value
- newValue := d.anchorValueMap[aliasName]
- if newValue.IsValid() {
- return newValue, nil
+ value := d.anchorValueMap[aliasName]
+ if value.IsValid() {
+ v, err := d.castToAssignableValue(value, typ, node)
+ if err == nil {
+ return v, nil
+ }
+ }
+ anchor, exists := d.anchorNodeMap[aliasName]
+ if exists {
+ node = anchor
}
}
+ var newValue reflect.Value
if node.Type() == ast.NullType {
- return reflect.Zero(typ), nil
+ newValue = reflect.New(typ).Elem()
+ } else {
+ newValue = d.createDecodableValue(typ)
}
- newValue := d.createDecodableValue(typ)
for defaultVal.Kind() == reflect.Ptr {
defaultVal = defaultVal.Elem()
}
if defaultVal.IsValid() && defaultVal.Type().AssignableTo(newValue.Type()) {
newValue.Set(defaultVal)
}
- if err := d.decodeValue(ctx, newValue, node); err != nil {
- return newValue, errors.Wrapf(err, "failed to decode value")
+ if node.Type() != ast.NullType {
+ if err := d.decodeValue(ctx, newValue, node); err != nil {
+ return reflect.Value{}, err
+ }
}
- return newValue, nil
+ return d.castToAssignableValue(newValue, typ, node)
}
-func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) {
- mapNode, err := d.getMapNode(node)
+func (d *Decoder) keyToNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return nil, ErrExceededMaxDepth
+ }
+
+ mapNode, err := d.getMapNode(node, false)
if err != nil {
- return nil, errors.Wrapf(err, "failed to get map node")
+ return nil, err
}
keyMap := map[string]struct{}{}
keyToNodeMap := map[string]ast.Node{}
- if mapNode == nil {
- return keyToNodeMap, nil
- }
mapIter := mapNode.MapRange()
for mapIter.Next() {
keyNode := mapIter.Key()
- if keyNode.Type() == ast.MergeKeyType {
+ if keyNode.IsMergeKey() {
if ignoreMergeKey {
continue
}
- mergeMap, err := d.keyToNodeMap(mapIter.Value(), ignoreMergeKey, getKeyOrValueNode)
+ mergeMap, err := d.keyToNodeMap(ctx, mapIter.Value(), ignoreMergeKey, getKeyOrValueNode)
if err != nil {
- return nil, errors.Wrapf(err, "failed to get keyToNodeMap by MergeKey node")
+ return nil, err
}
for k, v := range mergeMap {
if err := d.validateDuplicateKey(keyMap, k, v); err != nil {
- return nil, errors.Wrapf(err, "invalid struct key")
+ return nil, err
}
keyToNodeMap[k] = v
}
} else {
- key, ok := d.nodeToValue(keyNode).(string)
+ keyVal, err := d.nodeToValue(ctx, keyNode)
+ if err != nil {
+ return nil, err
+ }
+ key, ok := keyVal.(string)
if !ok {
- return nil, errors.Wrapf(err, "failed to decode map key")
+ return nil, err
}
if err := d.validateDuplicateKey(keyMap, key, keyNode); err != nil {
- return nil, errors.Wrapf(err, "invalid struct key")
+ return nil, err
}
keyToNodeMap[key] = getKeyOrValueNode(mapIter)
}
@@ -1046,30 +1166,33 @@ func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValue
return keyToNodeMap, nil
}
-func (d *Decoder) keyToKeyNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
- m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() })
+func (d *Decoder) keyToKeyNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
+ m, err := d.keyToNodeMap(ctx, node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() })
if err != nil {
- return nil, errors.Wrapf(err, "failed to get keyToNodeMap")
+ return nil, err
}
return m, nil
}
-func (d *Decoder) keyToValueNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
- m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() })
+func (d *Decoder) keyToValueNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
+ m, err := d.keyToNodeMap(ctx, node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() })
if err != nil {
- return nil, errors.Wrapf(err, "failed to get keyToNodeMap")
+ return nil, err
}
return m, nil
}
func (d *Decoder) setDefaultValueIfConflicted(v reflect.Value, fieldMap StructFieldMap) error {
+ for v.Type().Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
typ := v.Type()
if typ.Kind() != reflect.Struct {
return nil
}
embeddedStructFieldMap, err := structFieldMap(typ)
if err != nil {
- return errors.Wrapf(err, "failed to get struct field map by embedded type")
+ return err
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
@@ -1098,17 +1221,20 @@ var allowedTimestampFormats = []string{
"2006-1-2", // date only
}
-func (d *Decoder) castToTime(src ast.Node) (time.Time, error) {
+func (d *Decoder) castToTime(ctx context.Context, src ast.Node) (time.Time, error) {
if src == nil {
return time.Time{}, nil
}
- v := d.nodeToValue(src)
+ v, err := d.nodeToValue(ctx, src)
+ if err != nil {
+ return time.Time{}, err
+ }
if t, ok := v.(time.Time); ok {
return t, nil
}
s, ok := v.(string)
if !ok {
- return time.Time{}, errTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken())
+ return time.Time{}, errors.ErrTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken())
}
for _, format := range allowedTimestampFormats {
t, err := time.Parse(format, s)
@@ -1122,37 +1248,40 @@ func (d *Decoder) castToTime(src ast.Node) (time.Time, error) {
}
func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Node) error {
- t, err := d.castToTime(src)
+ t, err := d.castToTime(ctx, src)
if err != nil {
- return errors.Wrapf(err, "failed to convert to time")
+ return err
}
dst.Set(reflect.ValueOf(t))
return nil
}
-func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) {
+func (d *Decoder) castToDuration(ctx context.Context, src ast.Node) (time.Duration, error) {
if src == nil {
return 0, nil
}
- v := d.nodeToValue(src)
+ v, err := d.nodeToValue(ctx, src)
+ if err != nil {
+ return 0, err
+ }
if t, ok := v.(time.Duration); ok {
return t, nil
}
s, ok := v.(string)
if !ok {
- return 0, errTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken())
+ return 0, errors.ErrTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken())
}
t, err := time.ParseDuration(s)
if err != nil {
- return 0, errors.Wrapf(err, "failed to parse duration")
+ return 0, err
}
return t, nil
}
func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast.Node) error {
- t, err := d.castToDuration(src)
+ t, err := d.castToDuration(ctx, src)
if err != nil {
- return errors.Wrapf(err, "failed to convert to duration")
+ return err
}
dst.Set(reflect.ValueOf(t))
return nil
@@ -1160,18 +1289,15 @@ func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast
// getMergeAliasName support single alias only
func (d *Decoder) getMergeAliasName(src ast.Node) string {
- mapNode, err := d.getMapNode(src)
+ mapNode, err := d.getMapNode(src, true)
if err != nil {
return ""
}
- if mapNode == nil {
- return ""
- }
mapIter := mapNode.MapRange()
for mapIter.Next() {
key := mapIter.Key()
value := mapIter.Value()
- if key.Type() == ast.MergeKeyType && value.Type() == ast.AliasType {
+ if key.IsMergeKey() && value.Type() == ast.AliasType {
return value.(*ast.AliasNode).Value.GetToken().Value
}
}
@@ -1182,6 +1308,12 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
if src == nil {
return nil
}
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
+ }
+
structType := dst.Type()
srcValue := reflect.ValueOf(src)
srcType := srcValue.Type()
@@ -1196,18 +1328,18 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
}
structFieldMap, err := structFieldMap(structType)
if err != nil {
- return errors.Wrapf(err, "failed to create struct field map")
+ return err
}
ignoreMergeKey := structFieldMap.hasMergeProperty()
- keyToNodeMap, err := d.keyToValueNodeMap(src, ignoreMergeKey)
+ keyToNodeMap, err := d.keyToValueNodeMap(ctx, src, ignoreMergeKey)
if err != nil {
- return errors.Wrapf(err, "failed to get keyToValueNodeMap")
+ return err
}
var unknownFields map[string]ast.Node
if d.disallowUnknownField {
- unknownFields, err = d.keyToKeyNodeMap(src, ignoreMergeKey)
+ unknownFields, err = d.keyToKeyNodeMap(ctx, src, ignoreMergeKey)
if err != nil {
- return errors.Wrapf(err, "failed to get keyToKeyNodeMap")
+ return err
}
}
@@ -1226,13 +1358,17 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
if aliasName != "" {
newFieldValue := d.anchorValueMap[aliasName]
if newFieldValue.IsValid() {
- fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type()))
+ value, err := d.castToAssignableValue(newFieldValue, fieldValue.Type(), d.anchorNodeMap[aliasName])
+ if err != nil {
+ return err
+ }
+ fieldValue.Set(value)
}
}
continue
}
if !fieldValue.CanSet() {
- return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name)
+ return fmt.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name)
}
if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType {
// set nil value to pointer
@@ -1247,7 +1383,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, mapNode)
if d.disallowUnknownField {
if err := d.deleteStructKeys(fieldValue.Type(), unknownFields); err != nil {
- return errors.Wrapf(err, "cannot delete struct keys")
+ return err
}
}
@@ -1256,7 +1392,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
continue
}
var te *errors.TypeError
- if xerrors.As(err, &te) {
+ if errors.As(err, &te) {
if te.StructFieldName != nil {
fieldName := fmt.Sprintf("%s.%s", structType.Name(), *te.StructFieldName)
te.StructFieldName = &fieldName
@@ -1271,8 +1407,8 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
}
continue
}
- d.setDefaultValueIfConflicted(newFieldValue, structFieldMap)
- fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type()))
+ _ = d.setDefaultValueIfConflicted(newFieldValue, structFieldMap)
+ fieldValue.Set(newFieldValue)
continue
}
v, exists := keyToNodeMap[structField.RenderName]
@@ -1292,7 +1428,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
continue
}
var te *errors.TypeError
- if xerrors.As(err, &te) {
+ if errors.As(err, &te) {
fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name)
te.StructFieldName = &fieldName
foundErr = te
@@ -1301,17 +1437,26 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
}
continue
}
- fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type()))
+ fieldValue.Set(newFieldValue)
}
if foundErr != nil {
- return errors.Wrapf(foundErr, "failed to decode value")
+ return foundErr
}
// Ignore unknown fields when parsing an inline struct (recognized by a nil token).
// Unknown fields are expected (they could be fields from the parent struct).
if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil {
for key, node := range unknownFields {
- return errUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken())
+ var ok bool
+ for _, prefix := range d.allowedFieldPrefixes {
+ if strings.HasPrefix(key, prefix) {
+ ok = true
+ break
+ }
+ }
+ if !ok {
+ return errors.ErrUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken())
+ }
}
}
@@ -1332,7 +1477,10 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
node, exists := keyToNodeMap[structField.RenderName]
if exists {
// TODO: to make FieldError message cutomizable
- return errors.ErrSyntax(fmt.Sprintf("%s", err), node.GetToken())
+ return errors.ErrSyntax(
+ fmt.Sprintf("%s", err),
+ d.getParentMapTokenIfExistsForValidationError(node.Type(), node.GetToken()),
+ )
} else if t := src.GetToken(); t != nil && t.Prev != nil && t.Prev.Prev != nil {
// A missing required field will not be in the keyToNodeMap
// the error needs to be associated with the parent of the source node
@@ -1346,10 +1494,47 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
return nil
}
+// getParentMapTokenIfExists if the NodeType is a container type such as MappingType or SequenceType,
+// it is necessary to return the parent MapNode's colon token to represent the entire container.
+func (d *Decoder) getParentMapTokenIfExistsForValidationError(typ ast.NodeType, tk *token.Token) *token.Token {
+ if tk == nil {
+ return nil
+ }
+ if typ == ast.MappingType {
+ // map:
+ // key: value
+ // ^ current token ( colon )
+ if tk.Prev == nil {
+ return tk
+ }
+ key := tk.Prev
+ if key.Prev == nil {
+ return tk
+ }
+ return key.Prev
+ }
+ if typ == ast.SequenceType {
+ // map:
+ // - value
+ // ^ current token ( sequence entry )
+ if tk.Prev == nil {
+ return tk
+ }
+ return tk.Prev
+ }
+ return tk
+}
+
func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.Node) error {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
+ }
+
arrayNode, err := d.getArrayNode(src)
if err != nil {
- return errors.Wrapf(err, "failed to get array node")
+ return err
}
if arrayNode == nil {
return nil
@@ -1373,23 +1558,28 @@ func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.No
foundErr = err
}
continue
- } else {
- arrayValue.Index(idx).Set(d.castToAssignableValue(dstValue, elemType))
}
+ arrayValue.Index(idx).Set(dstValue)
}
idx++
}
dst.Set(arrayValue)
if foundErr != nil {
- return errors.Wrapf(foundErr, "failed to decode value")
+ return foundErr
}
return nil
}
func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.Node) error {
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
+ }
+
arrayNode, err := d.getArrayNode(src)
if err != nil {
- return errors.Wrapf(err, "failed to get array node")
+ return err
}
if arrayNode == nil {
return nil
@@ -1414,22 +1604,25 @@ func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.No
}
continue
}
- sliceValue = reflect.Append(sliceValue, d.castToAssignableValue(dstValue, elemType))
+ sliceValue = reflect.Append(sliceValue, dstValue)
}
dst.Set(sliceValue)
if foundErr != nil {
- return errors.Wrapf(foundErr, "failed to decode value")
+ return foundErr
}
return nil
}
func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node) error {
- mapNode, err := d.getMapNode(src)
- if err != nil {
- return errors.Wrapf(err, "failed to get map node")
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
}
- if mapNode == nil {
- return nil
+
+ mapNode, err := d.getMapNode(src, isMerge(ctx))
+ if err != nil {
+ return err
}
mapIter := mapNode.MapRange()
if !mapIter.Next() {
@@ -1437,16 +1630,21 @@ func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node)
}
key := mapIter.Key()
value := mapIter.Value()
- if key.Type() == ast.MergeKeyType {
- if err := d.decodeMapItem(ctx, dst, value); err != nil {
- return errors.Wrapf(err, "failed to decode map with merge key")
+ if key.IsMergeKey() {
+ if err := d.decodeMapItem(withMerge(ctx), dst, value); err != nil {
+ return err
}
return nil
}
- *dst = MapItem{
- Key: d.nodeToValue(key),
- Value: d.nodeToValue(value),
+ k, err := d.nodeToValue(ctx, key)
+ if err != nil {
+ return err
+ }
+ v, err := d.nodeToValue(ctx, value)
+ if err != nil {
+ return err
}
+ *dst = MapItem{Key: k, Value: v}
return nil
}
@@ -1455,9 +1653,9 @@ func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface
if !ok {
return nil
}
- if d.disallowDuplicateKey {
+ if !d.allowDuplicateMapKey {
if _, exists := keyMap[k]; exists {
- return errDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken())
+ return errors.ErrDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken())
}
}
keyMap[k] = struct{}{}
@@ -1465,12 +1663,15 @@ func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface
}
func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Node) error {
- mapNode, err := d.getMapNode(src)
- if err != nil {
- return errors.Wrapf(err, "failed to get map node")
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
}
- if mapNode == nil {
- return nil
+
+ mapNode, err := d.getMapNode(src, isMerge(ctx))
+ if err != nil {
+ return err
}
mapSlice := MapSlice{}
mapIter := mapNode.MapRange()
@@ -1478,39 +1679,46 @@ func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Nod
for mapIter.Next() {
key := mapIter.Key()
value := mapIter.Value()
- if key.Type() == ast.MergeKeyType {
+ if key.IsMergeKey() {
var m MapSlice
- if err := d.decodeMapSlice(ctx, &m, value); err != nil {
- return errors.Wrapf(err, "failed to decode map with merge key")
+ if err := d.decodeMapSlice(withMerge(ctx), &m, value); err != nil {
+ return err
}
for _, v := range m {
if err := d.validateDuplicateKey(keyMap, v.Key, value); err != nil {
- return errors.Wrapf(err, "invalid map key")
+ return err
}
mapSlice = append(mapSlice, v)
}
continue
}
- k := d.nodeToValue(key)
+ k, err := d.nodeToValue(ctx, key)
+ if err != nil {
+ return err
+ }
if err := d.validateDuplicateKey(keyMap, k, key); err != nil {
- return errors.Wrapf(err, "invalid map key")
+ return err
}
- mapSlice = append(mapSlice, MapItem{
- Key: k,
- Value: d.nodeToValue(value),
- })
+ v, err := d.nodeToValue(ctx, value)
+ if err != nil {
+ return err
+ }
+ mapSlice = append(mapSlice, MapItem{Key: k, Value: v})
}
*dst = mapSlice
return nil
}
func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node) error {
- mapNode, err := d.getMapNode(src)
- if err != nil {
- return errors.Wrapf(err, "failed to get map node")
+ d.stepIn()
+ defer d.stepOut()
+ if d.isExceededMaxDepth() {
+ return ErrExceededMaxDepth
}
- if mapNode == nil {
- return nil
+
+ mapNode, err := d.getMapNode(src, isMerge(ctx))
+ if err != nil {
+ return err
}
mapType := dst.Type()
mapValue := reflect.MakeMap(mapType)
@@ -1522,14 +1730,14 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
for mapIter.Next() {
key := mapIter.Key()
value := mapIter.Value()
- if key.Type() == ast.MergeKeyType {
- if err := d.decodeMap(ctx, dst, value); err != nil {
- return errors.Wrapf(err, "failed to decode map with merge key")
+ if key.IsMergeKey() {
+ if err := d.decodeMap(withMerge(ctx), dst, value); err != nil {
+ return err
}
iter := dst.MapRange()
for iter.Next() {
if err := d.validateDuplicateKey(keyMap, iter.Key(), value); err != nil {
- return errors.Wrapf(err, "invalid map key")
+ return err
}
mapValue.SetMapIndex(iter.Key(), iter.Value())
}
@@ -1539,10 +1747,14 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
k := d.createDecodableValue(keyType)
if d.canDecodeByUnmarshaler(k) {
if err := d.decodeByUnmarshaler(ctx, k, key); err != nil {
- return errors.Wrapf(err, "failed to decode by unmarshaler")
+ return err
}
} else {
- k = reflect.ValueOf(d.nodeToValue(key))
+ keyVal, err := d.nodeToValue(ctx, key)
+ if err != nil {
+ return err
+ }
+ k = reflect.ValueOf(keyVal)
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)
}
@@ -1550,7 +1762,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
if k.IsValid() {
if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil {
- return errors.Wrapf(err, "invalid map key")
+ return err
}
}
if valueType.Kind() == reflect.Ptr && value.Type() == ast.NullType {
@@ -1566,14 +1778,20 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
}
if !k.IsValid() {
// expect nil key
- mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType))
+ mapValue.SetMapIndex(d.createDecodableValue(keyType), dstValue)
continue
}
- mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType))
+ if keyType.Kind() != k.Kind() {
+ return errors.ErrSyntax(
+ fmt.Sprintf("cannot convert %q type to %q type", k.Kind(), keyType.Kind()),
+ key.GetToken(),
+ )
+ }
+ mapValue.SetMapIndex(k, dstValue)
}
dst.Set(mapValue)
if foundErr != nil {
- return errors.Wrapf(foundErr, "failed to decode value")
+ return foundErr
}
return nil
}
@@ -1581,7 +1799,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
func (d *Decoder) fileToReader(file string) (io.Reader, error) {
reader, err := os.Open(file)
if err != nil {
- return nil, errors.Wrapf(err, "failed to open file")
+ return nil, err
}
return reader, nil
}
@@ -1601,7 +1819,7 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) {
pattern := fmt.Sprintf("%s/*", dir)
matches, err := filepath.Glob(pattern)
if err != nil {
- return nil, errors.Wrapf(err, "failed to get files by %s", pattern)
+ return nil, err
}
readers := []io.Reader{}
for _, match := range matches {
@@ -1610,7 +1828,7 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) {
}
reader, err := d.fileToReader(match)
if err != nil {
- return nil, errors.Wrapf(err, "failed to get reader")
+ return nil, err
}
readers = append(readers, reader)
}
@@ -1619,32 +1837,32 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) {
func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) {
readers := []io.Reader{}
- if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if err := filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error {
if !d.isYAMLFile(path) {
return nil
}
- reader, err := d.fileToReader(path)
- if err != nil {
- return errors.Wrapf(err, "failed to get reader")
+ reader, readerErr := d.fileToReader(path)
+ if readerErr != nil {
+ return readerErr
}
readers = append(readers, reader)
return nil
}); err != nil {
- return nil, errors.Wrapf(err, "interrupt walk in %s", dir)
+ return nil, err
}
return readers, nil
}
-func (d *Decoder) resolveReference() error {
+func (d *Decoder) resolveReference(ctx context.Context) error {
for _, opt := range d.opts {
if err := opt(d); err != nil {
- return errors.Wrapf(err, "failed to exec option")
+ return err
}
}
for _, file := range d.referenceFiles {
reader, err := d.fileToReader(file)
if err != nil {
- return errors.Wrapf(err, "failed to get reader")
+ return err
}
d.referenceReaders = append(d.referenceReaders, reader)
}
@@ -1652,13 +1870,13 @@ func (d *Decoder) resolveReference() error {
if !d.isRecursiveDir {
readers, err := d.readersUnderDir(dir)
if err != nil {
- return errors.Wrapf(err, "failed to get readers from under the %s", dir)
+ return err
}
d.referenceReaders = append(d.referenceReaders, readers...)
} else {
readers, err := d.readersUnderDirRecursive(dir)
if err != nil {
- return errors.Wrapf(err, "failed to get readers from under the %s", dir)
+ return err
}
d.referenceReaders = append(d.referenceReaders, readers...)
}
@@ -1666,32 +1884,46 @@ func (d *Decoder) resolveReference() error {
for _, reader := range d.referenceReaders {
bytes, err := io.ReadAll(reader)
if err != nil {
- return errors.Wrapf(err, "failed to read buffer")
+ return err
}
// assign new anchor definition to anchorMap
- if _, err := d.parse(bytes); err != nil {
- return errors.Wrapf(err, "failed to decode")
+ if _, err := d.parse(ctx, bytes); err != nil {
+ return err
}
}
d.isResolvedReference = true
return nil
}
-func (d *Decoder) parse(bytes []byte) (*ast.File, error) {
+func (d *Decoder) parse(ctx context.Context, bytes []byte) (*ast.File, error) {
var parseMode parser.Mode
if d.toCommentMap != nil {
parseMode = parser.ParseComments
}
- f, err := parser.ParseBytes(bytes, parseMode)
+ var opts []parser.Option
+ if d.allowDuplicateMapKey {
+ opts = append(opts, parser.AllowDuplicateMapKey())
+ }
+ f, err := parser.ParseBytes(bytes, parseMode, opts...)
if err != nil {
- return nil, errors.Wrapf(err, "failed to parse yaml")
+ return nil, err
}
normalizedFile := &ast.File{}
for _, doc := range f.Docs {
// try to decode ast.Node to value and map anchor value to anchorMap
- if v := d.nodeToValue(doc.Body); v != nil {
+ v, err := d.nodeToValue(ctx, doc.Body)
+ if err != nil {
+ return nil, err
+ }
+ if v != nil || (doc.Body != nil && doc.Body.Type() == ast.NullType) {
normalizedFile.Docs = append(normalizedFile.Docs, doc)
+ cm := CommentMap{}
+ maps.Copy(cm, d.toCommentMap)
+ d.commentMaps = append(d.commentMaps, cm)
+ }
+ for k := range d.toCommentMap {
+ delete(d.toCommentMap, k)
}
}
return normalizedFile, nil
@@ -1701,25 +1933,34 @@ func (d *Decoder) isInitialized() bool {
return d.parsedFile != nil
}
-func (d *Decoder) decodeInit() error {
+func (d *Decoder) decodeInit(ctx context.Context) error {
if !d.isResolvedReference {
- if err := d.resolveReference(); err != nil {
- return errors.Wrapf(err, "failed to resolve reference")
+ if err := d.resolveReference(ctx); err != nil {
+ return err
}
}
var buf bytes.Buffer
if _, err := io.Copy(&buf, d.reader); err != nil {
- return errors.Wrapf(err, "failed to copy from reader")
+ return err
}
- file, err := d.parse(buf.Bytes())
+ file, err := d.parse(ctx, buf.Bytes())
if err != nil {
- return errors.Wrapf(err, "failed to decode")
+ return err
}
d.parsedFile = file
return nil
}
func (d *Decoder) decode(ctx context.Context, v reflect.Value) error {
+ d.decodeDepth = 0
+ d.anchorValueMap = make(map[string]reflect.Value)
+ if len(d.parsedFile.Docs) == 0 {
+ // empty document.
+ dst := v.Elem()
+ if dst.IsValid() {
+ dst.Set(reflect.Zero(dst.Type()))
+ }
+ }
if len(d.parsedFile.Docs) <= d.streamIndex {
return io.EOF
}
@@ -1727,8 +1968,11 @@ func (d *Decoder) decode(ctx context.Context, v reflect.Value) error {
if body == nil {
return nil
}
+ if len(d.commentMaps) > d.streamIndex {
+ maps.Copy(d.toCommentMap, d.commentMaps[d.streamIndex])
+ }
if err := d.decodeValue(ctx, v.Elem(), body); err != nil {
- return errors.Wrapf(err, "failed to decode value")
+ return err
}
d.streamIndex++
return nil
@@ -1747,26 +1991,20 @@ func (d *Decoder) Decode(v interface{}) error {
// and stores it in the value pointed to by v with context.Context.
func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error {
rv := reflect.ValueOf(v)
- if rv.Type().Kind() != reflect.Ptr {
- return errors.ErrDecodeRequiredPointerType
+ if !rv.IsValid() || rv.Type().Kind() != reflect.Ptr {
+ return ErrDecodeRequiredPointerType
}
if d.isInitialized() {
if err := d.decode(ctx, rv); err != nil {
- if err == io.EOF {
- return err
- }
- return errors.Wrapf(err, "failed to decode")
+ return err
}
return nil
}
- if err := d.decodeInit(); err != nil {
- return errors.Wrapf(err, "failed to decodeInit")
+ if err := d.decodeInit(ctx); err != nil {
+ return err
}
if err := d.decode(ctx, rv); err != nil {
- if err == io.EOF {
- return err
- }
- return errors.Wrapf(err, "failed to decode")
+ return err
}
return nil
}
@@ -1780,17 +2018,19 @@ func (d *Decoder) DecodeFromNode(node ast.Node, v interface{}) error {
func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v interface{}) error {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Ptr {
- return errors.ErrDecodeRequiredPointerType
+ return ErrDecodeRequiredPointerType
}
if !d.isInitialized() {
- if err := d.decodeInit(); err != nil {
- return errors.Wrapf(err, "failed to decodInit")
+ if err := d.decodeInit(ctx); err != nil {
+ return err
}
}
// resolve references to the anchor on the same file
- d.nodeToValue(node)
+ if _, err := d.nodeToValue(ctx, node); err != nil {
+ return err
+ }
if err := d.decodeValue(ctx, rv.Elem(), node); err != nil {
- return errors.Wrapf(err, "failed to decode value")
+ return err
}
return nil
}
diff --git a/vendor/github.com/goccy/go-yaml/encode.go b/vendor/github.com/goccy/go-yaml/encode.go
index 3b9b2981..e810608c 100644
--- a/vendor/github.com/goccy/go-yaml/encode.go
+++ b/vendor/github.com/goccy/go-yaml/encode.go
@@ -17,7 +17,6 @@ import (
"github.com/goccy/go-yaml/parser"
"github.com/goccy/go-yaml/printer"
"github.com/goccy/go-yaml/token"
- "golang.org/x/xerrors"
)
const (
@@ -29,24 +28,29 @@ const (
type Encoder struct {
writer io.Writer
opts []EncodeOption
- indent int
- indentSequence bool
singleQuote bool
isFlowStyle bool
isJSONStyle bool
useJSONMarshaler bool
+ enableSmartAnchor bool
+ aliasRefToName map[uintptr]string
+ anchorRefToName map[uintptr]string
+ anchorNameMap map[string]struct{}
anchorCallback func(*ast.AnchorNode, interface{}) error
- anchorPtrToNameMap map[uintptr]string
- customMarshalerMap map[reflect.Type]func(interface{}) ([]byte, error)
+ customMarshalerMap map[reflect.Type]func(context.Context, interface{}) ([]byte, error)
+ omitZero bool
+ omitEmpty bool
+ autoInt bool
useLiteralStyleIfMultiline bool
commentMap map[*Path][]*Comment
written bool
- line int
- column int
- offset int
- indentNum int
- indentLevel int
+ line int
+ column int
+ offset int
+ indentNum int
+ indentLevel int
+ indentSequence bool
}
// NewEncoder returns a new encoder that writes to w.
@@ -55,12 +59,14 @@ func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder {
return &Encoder{
writer: w,
opts: opts,
- indent: DefaultIndentSpaces,
- anchorPtrToNameMap: map[uintptr]string{},
- customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){},
+ customMarshalerMap: map[reflect.Type]func(context.Context, interface{}) ([]byte, error){},
line: 1,
column: 1,
offset: 0,
+ indentNum: DefaultIndentSpaces,
+ anchorRefToName: make(map[uintptr]string),
+ anchorNameMap: make(map[string]struct{}),
+ aliasRefToName: make(map[uintptr]string),
}
}
@@ -84,19 +90,19 @@ func (e *Encoder) Encode(v interface{}) error {
func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error {
node, err := e.EncodeToNodeContext(ctx, v)
if err != nil {
- return errors.Wrapf(err, "failed to encode to node")
+ return err
}
if err := e.setCommentByCommentMap(node); err != nil {
- return errors.Wrapf(err, "failed to set comment by comment map")
+ return err
}
if !e.written {
e.written = true
} else {
// write document separator
- e.writer.Write([]byte("---\n"))
+ _, _ = e.writer.Write([]byte("---\n"))
}
var p printer.Printer
- e.writer.Write(p.PrintNode(node))
+ _, _ = e.writer.Write(p.PrintNode(node))
return nil
}
@@ -109,12 +115,19 @@ func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) {
func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) {
for _, opt := range e.opts {
if err := opt(e); err != nil {
- return nil, errors.Wrapf(err, "failed to run option for encoder")
+ return nil, err
}
}
+ if e.enableSmartAnchor {
+ // during the first encoding, store all mappings between alias addresses and their names.
+ if _, err := e.encodeValue(ctx, reflect.ValueOf(v), 1); err != nil {
+ return nil, err
+ }
+ e.clearSmartAnchorRef()
+ }
node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode value")
+ return nil, err
}
return node, nil
}
@@ -126,7 +139,7 @@ func (e *Encoder) setCommentByCommentMap(node ast.Node) error {
for path, comments := range e.commentMap {
n, err := path.FilterNode(node)
if err != nil {
- return errors.Wrapf(err, "failed to filter node")
+ return err
}
if n == nil {
continue
@@ -140,15 +153,15 @@ func (e *Encoder) setCommentByCommentMap(node ast.Node) error {
switch comment.Position {
case CommentHeadPosition:
if err := e.setHeadComment(node, n, commentGroup); err != nil {
- return errors.Wrapf(err, "failed to set head comment")
+ return err
}
case CommentLinePosition:
if err := e.setLineComment(node, n, commentGroup); err != nil {
- return errors.Wrapf(err, "failed to set line comment")
+ return err
}
case CommentFootPosition:
if err := e.setFootComment(node, n, commentGroup); err != nil {
- return errors.Wrapf(err, "failed to set foot comment")
+ return err
}
default:
return ErrUnknownCommentPositionType
@@ -166,11 +179,11 @@ func (e *Encoder) setHeadComment(node ast.Node, filtered ast.Node, comment *ast.
switch p := parent.(type) {
case *ast.MappingValueNode:
if err := p.SetComment(comment); err != nil {
- return errors.Wrapf(err, "failed to set comment")
+ return err
}
case *ast.MappingNode:
if err := p.SetComment(comment); err != nil {
- return errors.Wrapf(err, "failed to set comment")
+ return err
}
case *ast.SequenceNode:
if len(p.ValueHeadComments) == 0 {
@@ -196,11 +209,11 @@ func (e *Encoder) setLineComment(node ast.Node, filtered ast.Node, comment *ast.
// Line comment cannot be set for mapping value node.
// It should probably be set for the parent map node
if err := e.setLineCommentToParentMapNode(node, filtered, comment); err != nil {
- return errors.Wrapf(err, "failed to set line comment to parent node")
+ return err
}
default:
if err := filtered.SetComment(comment); err != nil {
- return errors.Wrapf(err, "failed to set comment")
+ return err
}
}
return nil
@@ -214,11 +227,11 @@ func (e *Encoder) setLineCommentToParentMapNode(node ast.Node, filtered ast.Node
switch p := parent.(type) {
case *ast.MappingValueNode:
if err := p.Key.SetComment(comment); err != nil {
- return errors.Wrapf(err, "failed to set comment")
+ return err
}
case *ast.MappingNode:
if err := p.SetComment(comment); err != nil {
- return errors.Wrapf(err, "failed to set comment")
+ return err
}
default:
return ErrUnsupportedLinePositionType(parent)
@@ -247,7 +260,7 @@ func (e *Encoder) setFootComment(node ast.Node, filtered ast.Node, comment *ast.
func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) {
f, err := parser.ParseBytes(doc, 0)
if err != nil {
- return nil, errors.Wrapf(err, "failed to parse yaml")
+ return nil, err
}
for _, docNode := range f.Docs {
if docNode.Body != nil {
@@ -288,7 +301,7 @@ func (e *Encoder) existsTypeInCustomMarshalerMap(t reflect.Type) bool {
return false
}
-func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(interface{}) ([]byte, error), bool) {
+func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(context.Context, interface{}) ([]byte, error), bool) {
if marshaler, exists := e.customMarshalerMap[t]; exists {
return marshaler, exists
}
@@ -318,7 +331,7 @@ func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool {
return true
case InterfaceMarshaler:
return true
- case time.Time:
+ case time.Time, *time.Time:
return true
case time.Duration:
return true
@@ -334,13 +347,13 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
iface := v.Interface()
if marshaler, exists := e.marshalerFromCustomMarshalerMap(v.Type()); exists {
- doc, err := marshaler(iface)
+ doc, err := marshaler(ctx, iface)
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalYAML")
+ return nil, err
}
node, err := e.encodeDocument(doc)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode document")
+ return nil, err
}
return node, nil
}
@@ -348,11 +361,11 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
if marshaler, ok := iface.(BytesMarshalerContext); ok {
doc, err := marshaler.MarshalYAML(ctx)
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalYAML")
+ return nil, err
}
node, err := e.encodeDocument(doc)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode document")
+ return nil, err
}
return node, nil
}
@@ -360,11 +373,11 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
if marshaler, ok := iface.(BytesMarshaler); ok {
doc, err := marshaler.MarshalYAML()
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalYAML")
+ return nil, err
}
node, err := e.encodeDocument(doc)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode document")
+ return nil, err
}
return node, nil
}
@@ -372,7 +385,7 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
if marshaler, ok := iface.(InterfaceMarshalerContext); ok {
marshalV, err := marshaler.MarshalYAML(ctx)
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalYAML")
+ return nil, err
}
return e.encodeValue(ctx, reflect.ValueOf(marshalV), column)
}
@@ -380,7 +393,7 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
if marshaler, ok := iface.(InterfaceMarshaler); ok {
marshalV, err := marshaler.MarshalYAML()
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalYAML")
+ return nil, err
}
return e.encodeValue(ctx, reflect.ValueOf(marshalV), column)
}
@@ -388,20 +401,21 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
if t, ok := iface.(time.Time); ok {
return e.encodeTime(t, column), nil
}
+ // Handle *time.Time explicitly since it implements TextMarshaler and shouldn't be treated as plain text
+ if t, ok := iface.(*time.Time); ok && t != nil {
+ return e.encodeTime(*t, column), nil
+ }
if t, ok := iface.(time.Duration); ok {
return e.encodeDuration(t, column), nil
}
if marshaler, ok := iface.(encoding.TextMarshaler); ok {
- doc, err := marshaler.MarshalText()
+ text, err := marshaler.MarshalText()
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalText")
- }
- node, err := e.encodeDocument(doc)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to encode document")
+ return nil, err
}
+ node := e.encodeString(string(text), column)
return node, nil
}
@@ -409,21 +423,21 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column
if marshaler, ok := iface.(jsonMarshaler); ok {
jsonBytes, err := marshaler.MarshalJSON()
if err != nil {
- return nil, errors.Wrapf(err, "failed to MarshalJSON")
+ return nil, err
}
doc, err := JSONToYAML(jsonBytes)
if err != nil {
- return nil, errors.Wrapf(err, "failed to convert json to yaml")
+ return nil, err
}
node, err := e.encodeDocument(doc)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode document")
+ return nil, err
}
return node, nil
}
}
- return nil, xerrors.Errorf("does not implemented Marshaler")
+ return nil, errors.New("does not implemented Marshaler")
}
func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) {
@@ -433,7 +447,7 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
if e.canEncodeByMarshaler(v) {
node, err := e.encodeByMarshaler(ctx, v, column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode by marshaler")
+ return nil, err
}
return node, nil
}
@@ -447,12 +461,8 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
case reflect.Float64:
return e.encodeFloat(v.Float(), 64), nil
case reflect.Ptr:
- anchorName := e.anchorPtrToNameMap[v.Pointer()]
- if anchorName != "" {
- aliasName := anchorName
- alias := ast.Alias(token.New("*", "*", e.pos(column)))
- alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
- return alias, nil
+ if value := e.encodePtrAnchor(v, column); value != nil {
+ return value, nil
}
return e.encodeValue(ctx, v.Elem(), column)
case reflect.Interface:
@@ -465,6 +475,9 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
if mapSlice, ok := v.Interface().(MapSlice); ok {
return e.encodeMapSlice(ctx, mapSlice, column)
}
+ if value := e.encodePtrAnchor(v, column); value != nil {
+ return value, nil
+ }
return e.encodeSlice(ctx, v)
case reflect.Array:
return e.encodeArray(ctx, v)
@@ -479,12 +492,27 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
}
return e.encodeStruct(ctx, v, column)
case reflect.Map:
- return e.encodeMap(ctx, v, column), nil
+ if value := e.encodePtrAnchor(v, column); value != nil {
+ return value, nil
+ }
+ return e.encodeMap(ctx, v, column)
default:
- return nil, xerrors.Errorf("unknown value type %s", v.Type().String())
+ return nil, fmt.Errorf("unknown value type %s", v.Type().String())
}
}
+func (e *Encoder) encodePtrAnchor(v reflect.Value, column int) ast.Node {
+ anchorName, exists := e.getAnchor(v.Pointer())
+ if !exists {
+ return nil
+ }
+ aliasName := anchorName
+ alias := ast.Alias(token.New("*", "*", e.pos(column)))
+ alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
+ e.setSmartAlias(aliasName, v.Pointer())
+ return alias
+}
+
func (e *Encoder) pos(column int) *token.Position {
return &token.Position{
Line: e.line,
@@ -501,12 +529,12 @@ func (e *Encoder) encodeNil() *ast.NullNode {
}
func (e *Encoder) encodeInt(v int64) *ast.IntegerNode {
- value := fmt.Sprint(v)
+ value := strconv.FormatInt(v, 10)
return ast.Integer(token.New(value, value, e.pos(e.column)))
}
func (e *Encoder) encodeUint(v uint64) *ast.IntegerNode {
- value := fmt.Sprint(v)
+ value := strconv.FormatUint(v, 10)
return ast.Integer(token.New(value, value, e.pos(e.column)))
}
@@ -523,6 +551,9 @@ func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node {
}
value := strconv.FormatFloat(v, 'g', -1, bitSize)
if !strings.Contains(value, ".") && !strings.Contains(value, "e") {
+ if e.autoInt {
+ return ast.Integer(token.New(value, value, e.pos(e.column)))
+ }
// append x.0 suffix to keep float value context
value = fmt.Sprintf("%s.0", value)
}
@@ -539,6 +570,17 @@ func (e *Encoder) isNeedQuoted(v string) bool {
if e.isFlowStyle && strings.ContainsAny(v, `]},'"`) {
return true
}
+ if e.isFlowStyle {
+ for i := 0; i < len(v); i++ {
+ if v[i] != ':' {
+ continue
+ }
+ if i+1 < len(v) && v[i+1] == '/' {
+ continue
+ }
+ return true
+ }
+ }
if token.IsNeedQuoted(v) {
return true
}
@@ -557,45 +599,41 @@ func (e *Encoder) encodeString(v string, column int) *ast.StringNode {
}
func (e *Encoder) encodeBool(v bool) *ast.BoolNode {
- value := fmt.Sprint(v)
+ value := strconv.FormatBool(v)
return ast.Bool(token.New(value, value, e.pos(e.column)))
}
func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) {
if e.indentSequence {
- e.column += e.indent
+ e.column += e.indentNum
+ defer func() { e.column -= e.indentNum }()
}
column := e.column
sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle)
for i := 0; i < value.Len(); i++ {
node, err := e.encodeValue(ctx, value.Index(i), column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode value for slice")
+ return nil, err
}
sequence.Values = append(sequence.Values, node)
}
- if e.indentSequence {
- e.column -= e.indent
- }
return sequence, nil
}
func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) {
if e.indentSequence {
- e.column += e.indent
+ e.column += e.indentNum
+ defer func() { e.column -= e.indentNum }()
}
column := e.column
sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle)
for i := 0; i < value.Len(); i++ {
node, err := e.encodeValue(ctx, value.Index(i), column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode value for array")
+ return nil, err
}
sequence.Values = append(sequence.Values, node)
}
- if e.indentSequence {
- e.column -= e.indent
- }
return sequence, nil
}
@@ -604,10 +642,13 @@ func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (
v := reflect.ValueOf(item.Value)
value, err := e.encodeValue(ctx, v, column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode MapItem")
+ return nil, err
}
if e.isMapNode(value) {
- value.AddColumn(e.indent)
+ value.AddColumn(e.indentNum)
+ }
+ if e.isTagAndMapNode(value) {
+ value.AddColumn(e.indentNum)
}
return ast.MappingValue(
token.New("", "", e.pos(column)),
@@ -619,11 +660,11 @@ func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (
func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (*ast.MappingNode, error) {
node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
for _, item := range value {
- value, err := e.encodeMapItem(ctx, item, column)
+ encoded, err := e.encodeMapItem(ctx, item, column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice")
+ return nil, err
}
- node.Values = append(node.Values, value)
+ node.Values = append(node.Values, encoded)
}
return node, nil
}
@@ -633,7 +674,12 @@ func (e *Encoder) isMapNode(node ast.Node) bool {
return ok
}
-func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node {
+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, error) {
node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
keys := make([]interface{}, len(value.MapKeys()))
for i, k := range value.MapKeys() {
@@ -645,20 +691,41 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
for _, key := range keys {
k := reflect.ValueOf(key)
v := value.MapIndex(k)
- value, err := e.encodeValue(ctx, v, column)
+ encoded, err := e.encodeValue(ctx, v, column)
if err != nil {
- return nil
+ return nil, err
}
- if e.isMapNode(value) {
- value.AddColumn(e.indent)
+ if e.isMapNode(encoded) {
+ encoded.AddColumn(e.indentNum)
+ }
+ if e.isTagAndMapNode(encoded) {
+ encoded.AddColumn(e.indentNum)
+ }
+ keyText := fmt.Sprint(key)
+ vRef := e.toPointer(v)
+
+ // during the second encoding, an anchor is assigned if it is found to be used by an alias.
+ if aliasName, exists := e.getSmartAlias(vRef); exists {
+ anchorName := aliasName
+ anchorNode := ast.Anchor(token.New("&", "&", e.pos(column)))
+ anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column)))
+ anchorNode.Value = encoded
+ encoded = anchorNode
+ }
+
+ kn, err := e.encodeValue(ctx, reflect.ValueOf(key), column)
+ keyNode, ok := kn.(ast.MapKeyNode)
+ if !ok || err != nil {
+ keyNode = e.encodeString(fmt.Sprint(key), column)
}
node.Values = append(node.Values, ast.MappingValue(
nil,
- e.encodeString(fmt.Sprint(key), column),
- value,
+ keyNode,
+ encoded,
))
+ e.setSmartAnchor(vRef, keyText)
}
- return node
+ return node, nil
}
// IsZeroer is used to check whether an object is zero to determine
@@ -668,7 +735,7 @@ type IsZeroer interface {
IsZero() bool
}
-func (e *Encoder) isZeroValue(v reflect.Value) bool {
+func (e *Encoder) isOmittedByOmitZero(v reflect.Value) bool {
kind := v.Kind()
if z, ok := v.Interface().(IsZeroer); ok {
if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
@@ -677,13 +744,74 @@ func (e *Encoder) isZeroValue(v reflect.Value) bool {
return z.IsZero()
}
switch kind {
+ case reflect.String:
+ return len(v.String()) == 0
+ case reflect.Interface, reflect.Ptr, reflect.Slice, reflect.Map:
+ return v.IsNil()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Struct:
+ vt := v.Type()
+ for i := v.NumField() - 1; i >= 0; i-- {
+ if vt.Field(i).PkgPath != "" {
+ continue // private field
+ }
+ if !e.isOmittedByOmitZero(v.Field(i)) {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
+
+func (e *Encoder) isOmittedByOmitEmptyOption(v reflect.Value) bool {
+ switch v.Kind() {
case reflect.String:
return len(v.String()) == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
- case reflect.Slice:
+ case reflect.Slice, reflect.Map:
return v.Len() == 0
- case reflect.Map:
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ }
+ return false
+}
+
+// The current implementation of the omitempty tag combines the functionality of encoding/json's omitempty and omitzero tags.
+// This stems from a historical decision to respect the implementation of gopkg.in/yaml.v2, but it has caused confusion,
+// so we are working to integrate it into the functionality of encoding/json. (However, this will take some time.)
+// In the current implementation, in addition to the exclusion conditions of omitempty,
+// if a type implements IsZero, that implementation will be used.
+// Furthermore, for non-pointer structs, if all fields are eligible for exclusion,
+// the struct itself will also be excluded. These behaviors are originally the functionality of omitzero.
+func (e *Encoder) isOmittedByOmitEmptyTag(v reflect.Value) bool {
+ kind := v.Kind()
+ if z, ok := v.Interface().(IsZeroer); ok {
+ if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
+ return true
+ }
+ return z.IsZero()
+ }
+ switch kind {
+ case reflect.String:
+ return len(v.String()) == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ case reflect.Slice, reflect.Map:
return v.Len() == 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
@@ -699,7 +827,7 @@ func (e *Encoder) isZeroValue(v reflect.Value) bool {
if vt.Field(i).PkgPath != "" {
continue // private field
}
- if !e.isZeroValue(v.Field(i)) {
+ if !e.isOmittedByOmitEmptyTag(v.Field(i)) {
return false
}
}
@@ -730,14 +858,14 @@ func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue ref
anchorNode.Value = value
if e.anchorCallback != nil {
if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil {
- return nil, errors.Wrapf(err, "failed to marshal anchor")
+ return nil, err
}
if snode, ok := anchorNode.Name.(*ast.StringNode); ok {
anchorName = snode.Value
}
}
if fieldValue.Kind() == reflect.Ptr {
- e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName
+ e.setAnchor(fieldValue.Pointer(), anchorName)
}
return anchorNode, nil
}
@@ -745,9 +873,9 @@ func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue ref
func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) {
node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
structType := value.Type()
- structFieldMap, err := structFieldMap(structType)
+ fieldMap, err := structFieldMap(structType)
if err != nil {
- return nil, errors.Wrapf(err, "failed to get struct field map")
+ return nil, err
}
hasInlineAnchorField := false
var inlineAnchorValue reflect.Value
@@ -757,119 +885,190 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
continue
}
fieldValue := value.FieldByName(field.Name)
- structField := structFieldMap[field.Name]
- if structField.IsOmitEmpty && e.isZeroValue(fieldValue) {
- // omit encoding
+ sf := fieldMap[field.Name]
+ if (e.omitZero || sf.IsOmitZero) && e.isOmittedByOmitZero(fieldValue) {
+ // omit encoding by omitzero tag or OmitZero option.
+ continue
+ }
+ if e.omitEmpty && e.isOmittedByOmitEmptyOption(fieldValue) {
+ // omit encoding by OmitEmpty option.
+ continue
+ }
+ if sf.IsOmitEmpty && e.isOmittedByOmitEmptyTag(fieldValue) {
+ // omit encoding by omitempty tag.
continue
}
ve := e
- if !e.isFlowStyle && structField.IsFlow {
+ if !e.isFlowStyle && sf.IsFlow {
ve = &Encoder{}
*ve = *e
ve.isFlowStyle = true
}
- value, err := ve.encodeValue(ctx, fieldValue, column)
+ encoded, err := ve.encodeValue(ctx, fieldValue, column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode value")
+ return nil, err
}
- if e.isMapNode(value) {
- value.AddColumn(e.indent)
+ if e.isMapNode(encoded) {
+ encoded.AddColumn(e.indentNum)
}
- var key ast.MapKeyNode = e.encodeString(structField.RenderName, column)
+ var key ast.MapKeyNode = e.encodeString(sf.RenderName, column)
switch {
- case structField.AnchorName != "":
- anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to encode anchor")
- }
- value = anchorNode
- case structField.IsAutoAlias:
- if fieldValue.Kind() != reflect.Ptr {
- return nil, xerrors.Errorf(
- "%s in struct is not pointer type. but required automatically alias detection",
- structField.FieldName,
- )
- }
- anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()]
- if anchorName == "" {
- return nil, xerrors.Errorf(
- "cannot find anchor name from pointer address for automatically alias detection",
- )
+ case encoded.Type() == ast.AliasType:
+ if aliasName := sf.AliasName; aliasName != "" {
+ alias, ok := encoded.(*ast.AliasNode)
+ if !ok {
+ return nil, errors.ErrUnexpectedNodeType(encoded.Type(), ast.AliasType, encoded.GetToken())
+ }
+ got := alias.Value.String()
+ if aliasName != got {
+ return nil, fmt.Errorf("expected alias name is %q but got %q", aliasName, got)
+ }
}
- aliasName := anchorName
- alias := ast.Alias(token.New("*", "*", e.pos(column)))
- alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
- value = alias
- if structField.IsInline {
+ if sf.IsInline {
// if both used alias and inline, output `<<: *alias`
key = ast.MergeKey(token.New("<<", "<<", e.pos(column)))
}
- case structField.AliasName != "":
- aliasName := structField.AliasName
- alias := ast.Alias(token.New("*", "*", e.pos(column)))
- alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
- value = alias
- if structField.IsInline {
- // if both used alias and inline, output `<<: *alias`
- key = ast.MergeKey(token.New("<<", "<<", e.pos(column)))
+ case sf.AnchorName != "":
+ anchorNode, err := e.encodeAnchor(sf.AnchorName, encoded, fieldValue, column)
+ if err != nil {
+ return nil, err
}
- case structField.IsInline:
- isAutoAnchor := structField.IsAutoAnchor
+ encoded = anchorNode
+ case sf.IsInline:
+ isAutoAnchor := sf.IsAutoAnchor
if !hasInlineAnchorField {
hasInlineAnchorField = isAutoAnchor
}
if isAutoAnchor {
inlineAnchorValue = fieldValue
}
- mapNode, ok := value.(ast.MapNode)
+ mapNode, ok := encoded.(ast.MapNode)
if !ok {
// if an inline field is null, skip encoding it
- if _, ok := value.(*ast.NullNode); ok {
+ if _, ok := encoded.(*ast.NullNode); ok {
continue
}
- return nil, xerrors.Errorf("inline value is must be map or struct type")
+ return nil, errors.New("inline value is must be map or struct type")
}
mapIter := mapNode.MapRange()
for mapIter.Next() {
- key := mapIter.Key()
- value := mapIter.Value()
- keyName := key.GetToken().Value
- if structFieldMap.isIncludedRenderName(keyName) {
- // if declared same key name, skip encoding this field
+ mapKey := mapIter.Key()
+ mapValue := mapIter.Value()
+ keyName := mapKey.GetToken().Value
+ if fieldMap.isIncludedRenderName(keyName) {
+ // if declared the same key name, skip encoding this field
continue
}
- key.AddColumn(-e.indent)
- value.AddColumn(-e.indent)
- node.Values = append(node.Values, ast.MappingValue(nil, key, value))
+ mapKey.AddColumn(-e.indentNum)
+ mapValue.AddColumn(-e.indentNum)
+ node.Values = append(node.Values, ast.MappingValue(nil, mapKey, mapValue))
}
continue
- case structField.IsAutoAnchor:
- anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column)
+ case sf.IsAutoAnchor:
+ anchorNode, err := e.encodeAnchor(sf.RenderName, encoded, fieldValue, column)
if err != nil {
- return nil, errors.Wrapf(err, "failed to encode anchor")
+ return nil, err
}
- value = anchorNode
+ encoded = anchorNode
}
- node.Values = append(node.Values, ast.MappingValue(nil, key, value))
+ node.Values = append(node.Values, ast.MappingValue(nil, key, encoded))
}
if hasInlineAnchorField {
- node.AddColumn(e.indent)
+ node.AddColumn(e.indentNum)
anchorName := "anchor"
anchorNode := ast.Anchor(token.New("&", "&", e.pos(column)))
anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column)))
anchorNode.Value = node
if e.anchorCallback != nil {
if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil {
- return nil, errors.Wrapf(err, "failed to marshal anchor")
+ return nil, err
}
if snode, ok := anchorNode.Name.(*ast.StringNode); ok {
anchorName = snode.Value
}
}
if inlineAnchorValue.Kind() == reflect.Ptr {
- e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName
+ e.setAnchor(inlineAnchorValue.Pointer(), anchorName)
}
return anchorNode, nil
}
return node, nil
}
+
+func (e *Encoder) toPointer(v reflect.Value) uintptr {
+ if e.isInvalidValue(v) {
+ return 0
+ }
+
+ switch v.Type().Kind() {
+ case reflect.Ptr:
+ return v.Pointer()
+ case reflect.Interface:
+ return e.toPointer(v.Elem())
+ case reflect.Slice:
+ return v.Pointer()
+ case reflect.Map:
+ return v.Pointer()
+ }
+ return 0
+}
+
+func (e *Encoder) clearSmartAnchorRef() {
+ if !e.enableSmartAnchor {
+ return
+ }
+ e.anchorRefToName = make(map[uintptr]string)
+ e.anchorNameMap = make(map[string]struct{})
+}
+
+func (e *Encoder) setSmartAnchor(ptr uintptr, name string) {
+ if !e.enableSmartAnchor {
+ return
+ }
+ e.setAnchor(ptr, e.generateAnchorName(name))
+}
+
+func (e *Encoder) setAnchor(ptr uintptr, name string) {
+ if ptr == 0 {
+ return
+ }
+ if name == "" {
+ return
+ }
+ e.anchorRefToName[ptr] = name
+ e.anchorNameMap[name] = struct{}{}
+}
+
+func (e *Encoder) generateAnchorName(base string) string {
+ if _, exists := e.anchorNameMap[base]; !exists {
+ return base
+ }
+ for i := 1; i < 100; i++ {
+ name := base + strconv.Itoa(i)
+ if _, exists := e.anchorNameMap[name]; exists {
+ continue
+ }
+ return name
+ }
+ return ""
+}
+
+func (e *Encoder) getAnchor(ref uintptr) (string, bool) {
+ anchorName, exists := e.anchorRefToName[ref]
+ return anchorName, exists
+}
+
+func (e *Encoder) setSmartAlias(name string, ref uintptr) {
+ if !e.enableSmartAnchor {
+ return
+ }
+ e.aliasRefToName[ref] = name
+}
+
+func (e *Encoder) getSmartAlias(ref uintptr) (string, bool) {
+ if !e.enableSmartAnchor {
+ return "", false
+ }
+ aliasName, exists := e.aliasRefToName[ref]
+ return aliasName, exists
+}
diff --git a/vendor/github.com/goccy/go-yaml/error.go b/vendor/github.com/goccy/go-yaml/error.go
index 163dcc55..52d3e7e6 100644
--- a/vendor/github.com/goccy/go-yaml/error.go
+++ b/vendor/github.com/goccy/go-yaml/error.go
@@ -1,62 +1,77 @@
package yaml
import (
+ "fmt"
+
"github.com/goccy/go-yaml/ast"
- "golang.org/x/xerrors"
+ "github.com/goccy/go-yaml/internal/errors"
)
var (
- ErrInvalidQuery = xerrors.New("invalid query")
- ErrInvalidPath = xerrors.New("invalid path instance")
- 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")
+ ErrInvalidQuery = errors.New("invalid query")
+ ErrInvalidPath = errors.New("invalid path instance")
+ ErrInvalidPathString = errors.New("invalid path string")
+ ErrNotFoundNode = errors.New("node not found")
+ ErrUnknownCommentPositionType = errors.New("unknown comment position type")
+ ErrInvalidCommentMapValue = errors.New("invalid comment map value. it must be not nil value")
+ ErrDecodeRequiredPointerType = errors.New("required pointer type value")
+ ErrExceededMaxDepth = errors.New("exceeded max depth")
+ FormatErrorWithToken = errors.FormatError
+)
+
+type (
+ SyntaxError = errors.SyntaxError
+ TypeError = errors.TypeError
+ OverflowError = errors.OverflowError
+ DuplicateKeyError = errors.DuplicateKeyError
+ UnknownFieldError = errors.UnknownFieldError
+ UnexpectedNodeTypeError = errors.UnexpectedNodeTypeError
+ Error = errors.Error
)
func ErrUnsupportedHeadPositionType(node ast.Node) error {
- return xerrors.Errorf("unsupported comment head position for %s", node.Type())
+ return fmt.Errorf("unsupported comment head position for %s", node.Type())
}
func ErrUnsupportedLinePositionType(node ast.Node) error {
- return xerrors.Errorf("unsupported comment line position for %s", node.Type())
+ return fmt.Errorf("unsupported comment line position for %s", node.Type())
}
func ErrUnsupportedFootPositionType(node ast.Node) error {
- return xerrors.Errorf("unsupported comment foot position for %s", node.Type())
+ return fmt.Errorf("unsupported comment foot position for %s", node.Type())
}
// IsInvalidQueryError whether err is ErrInvalidQuery or not.
func IsInvalidQueryError(err error) bool {
- return xerrors.Is(err, ErrInvalidQuery)
+ return errors.Is(err, ErrInvalidQuery)
}
// IsInvalidPathError whether err is ErrInvalidPath or not.
func IsInvalidPathError(err error) bool {
- return xerrors.Is(err, ErrInvalidPath)
+ return errors.Is(err, ErrInvalidPath)
}
// IsInvalidPathStringError whether err is ErrInvalidPathString or not.
func IsInvalidPathStringError(err error) bool {
- return xerrors.Is(err, ErrInvalidPathString)
+ return errors.Is(err, ErrInvalidPathString)
}
// IsNotFoundNodeError whether err is ErrNotFoundNode or not.
func IsNotFoundNodeError(err error) bool {
- return xerrors.Is(err, ErrNotFoundNode)
+ return errors.Is(err, ErrNotFoundNode)
}
// IsInvalidTokenTypeError whether err is ast.ErrInvalidTokenType or not.
func IsInvalidTokenTypeError(err error) bool {
- return xerrors.Is(err, ast.ErrInvalidTokenType)
+ return errors.Is(err, ast.ErrInvalidTokenType)
}
// IsInvalidAnchorNameError whether err is ast.ErrInvalidAnchorName or not.
func IsInvalidAnchorNameError(err error) bool {
- return xerrors.Is(err, ast.ErrInvalidAnchorName)
+ return errors.Is(err, ast.ErrInvalidAnchorName)
}
// IsInvalidAliasNameError whether err is ast.ErrInvalidAliasName or not.
func IsInvalidAliasNameError(err error) bool {
- return xerrors.Is(err, ast.ErrInvalidAliasName)
+ return errors.Is(err, ast.ErrInvalidAliasName)
}
diff --git a/vendor/github.com/goccy/go-yaml/internal/errors/error.go b/vendor/github.com/goccy/go-yaml/internal/errors/error.go
index 7f1ea9af..b08a3fc6 100644
--- a/vendor/github.com/goccy/go-yaml/internal/errors/error.go
+++ b/vendor/github.com/goccy/go-yaml/internal/errors/error.go
@@ -1,260 +1,246 @@
package errors
import (
- "bytes"
+ "errors"
"fmt"
"reflect"
+ "github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/printer"
"github.com/goccy/go-yaml/token"
- "golang.org/x/xerrors"
+)
+
+var (
+ As = errors.As
+ Is = errors.Is
+ New = errors.New
)
const (
- defaultColorize = false
+ defaultFormatColor = false
defaultIncludeSource = true
)
+type Error interface {
+ error
+ GetToken() *token.Token
+ GetMessage() string
+ FormatError(bool, bool) string
+}
+
var (
- ErrDecodeRequiredPointerType = xerrors.New("required pointer type value")
+ _ Error = new(SyntaxError)
+ _ Error = new(TypeError)
+ _ Error = new(OverflowError)
+ _ Error = new(DuplicateKeyError)
+ _ Error = new(UnknownFieldError)
+ _ Error = new(UnexpectedNodeTypeError)
)
-// Wrapf wrap error for stack trace
-func Wrapf(err error, msg string, args ...interface{}) error {
- return &wrapError{
- baseError: &baseError{},
- err: xerrors.Errorf(msg, args...),
- nextErr: err,
- frame: xerrors.Caller(1),
- }
+type SyntaxError struct {
+ Message string
+ Token *token.Token
+}
+
+type TypeError struct {
+ DstType reflect.Type
+ SrcType reflect.Type
+ StructFieldName *string
+ Token *token.Token
+}
+
+type OverflowError struct {
+ DstType reflect.Type
+ SrcNum string
+ Token *token.Token
+}
+
+type DuplicateKeyError struct {
+ Message string
+ Token *token.Token
+}
+
+type UnknownFieldError struct {
+ Message string
+ Token *token.Token
+}
+
+type UnexpectedNodeTypeError struct {
+ Actual ast.NodeType
+ Expected ast.NodeType
+ Token *token.Token
}
// ErrSyntax create syntax error instance with message and token
-func ErrSyntax(msg string, tk *token.Token) *syntaxError {
- return &syntaxError{
- baseError: &baseError{},
- msg: msg,
- token: tk,
- frame: xerrors.Caller(1),
+func ErrSyntax(msg string, tk *token.Token) *SyntaxError {
+ return &SyntaxError{
+ Message: msg,
+ Token: tk,
}
}
-type baseError struct {
- state fmt.State
- verb rune
+// ErrOverflow creates an overflow error instance with message and a token.
+func ErrOverflow(dstType reflect.Type, num string, tk *token.Token) *OverflowError {
+ return &OverflowError{
+ DstType: dstType,
+ SrcNum: num,
+ Token: tk,
+ }
}
-func (e *baseError) Error() string {
- return ""
+// ErrTypeMismatch cerates an type mismatch error instance with token.
+func ErrTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *TypeError {
+ return &TypeError{
+ DstType: dstType,
+ SrcType: srcType,
+ Token: token,
+ }
}
-func (e *baseError) chainStateAndVerb(err error) {
- wrapErr, ok := err.(*wrapError)
- if ok {
- wrapErr.state = e.state
- wrapErr.verb = e.verb
+// ErrDuplicateKey creates an duplicate key error instance with token.
+func ErrDuplicateKey(msg string, tk *token.Token) *DuplicateKeyError {
+ return &DuplicateKeyError{
+ Message: msg,
+ Token: tk,
}
- syntaxErr, ok := err.(*syntaxError)
- if ok {
- syntaxErr.state = e.state
- syntaxErr.verb = e.verb
+}
+
+// ErrUnknownField creates an unknown field error instance with token.
+func ErrUnknownField(msg string, tk *token.Token) *UnknownFieldError {
+ return &UnknownFieldError{
+ Message: msg,
+ Token: tk,
}
}
-type wrapError struct {
- *baseError
- err error
- nextErr error
- frame xerrors.Frame
+func ErrUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) *UnexpectedNodeTypeError {
+ return &UnexpectedNodeTypeError{
+ Actual: actual,
+ Expected: expected,
+ Token: tk,
+ }
}
-type FormatErrorPrinter struct {
- xerrors.Printer
- Colored bool
- InclSource bool
+func (e *SyntaxError) GetMessage() string {
+ return e.Message
}
-func (e *wrapError) As(target interface{}) bool {
- err := e.nextErr
- for {
- if wrapErr, ok := err.(*wrapError); ok {
- err = wrapErr.nextErr
- continue
- }
- break
- }
- return xerrors.As(err, target)
+func (e *SyntaxError) GetToken() *token.Token {
+ return e.Token
}
-func (e *wrapError) Unwrap() error {
- return e.nextErr
+func (e *SyntaxError) Error() string {
+ return e.FormatError(defaultFormatColor, defaultIncludeSource)
}
-func (e *wrapError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
- return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
+func (e *SyntaxError) FormatError(colored, inclSource bool) string {
+ return FormatError(e.Message, e.Token, colored, inclSource)
}
-func (e *wrapError) FormatError(p xerrors.Printer) error {
- if _, ok := p.(*FormatErrorPrinter); !ok {
- p = &FormatErrorPrinter{
- Printer: p,
- Colored: defaultColorize,
- InclSource: defaultIncludeSource,
- }
- }
- if e.verb == 'v' && e.state.Flag('+') {
- // print stack trace for debugging
- p.Print(e.err, "\n")
- e.frame.Format(p)
- e.chainStateAndVerb(e.nextErr)
- return e.nextErr
- }
- err := e.nextErr
- for {
- if wrapErr, ok := err.(*wrapError); ok {
- err = wrapErr.nextErr
- continue
- }
- break
- }
- e.chainStateAndVerb(err)
- if fmtErr, ok := err.(xerrors.Formatter); ok {
- fmtErr.FormatError(p)
- } else {
- p.Print(err)
- }
- return nil
+func (e *OverflowError) GetMessage() string {
+ return e.msg()
}
-type wrapState struct {
- org fmt.State
+func (e *OverflowError) GetToken() *token.Token {
+ return e.Token
}
-func (s *wrapState) Write(b []byte) (n int, err error) {
- return s.org.Write(b)
+func (e *OverflowError) Error() string {
+ return e.FormatError(defaultFormatColor, defaultIncludeSource)
}
-func (s *wrapState) Width() (wid int, ok bool) {
- return s.org.Width()
+func (e *OverflowError) FormatError(colored, inclSource bool) string {
+ return FormatError(e.msg(), e.Token, colored, inclSource)
}
-func (s *wrapState) Precision() (prec int, ok bool) {
- return s.org.Precision()
+func (e *OverflowError) msg() string {
+ return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.SrcNum, e.DstType)
}
-func (s *wrapState) Flag(c int) bool {
- // set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text
- if c == '#' {
- // ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail.
- // ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 )
- return false
+func (e *TypeError) msg() string {
+ if e.StructFieldName != nil {
+ return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType)
}
- return true
+ return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType)
}
-func (e *wrapError) Format(state fmt.State, verb rune) {
- e.state = state
- e.verb = verb
- xerrors.FormatError(e, &wrapState{org: state}, verb)
+func (e *TypeError) GetMessage() string {
+ return e.msg()
}
-func (e *wrapError) Error() string {
- var buf bytes.Buffer
- e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource)
- return buf.String()
+func (e *TypeError) GetToken() *token.Token {
+ return e.Token
}
-type syntaxError struct {
- *baseError
- msg string
- token *token.Token
- frame xerrors.Frame
+func (e *TypeError) Error() string {
+ return e.FormatError(defaultFormatColor, defaultIncludeSource)
}
-func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
- return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
+func (e *TypeError) FormatError(colored, inclSource bool) string {
+ return FormatError(e.msg(), e.Token, colored, inclSource)
}
-func (e *syntaxError) FormatError(p xerrors.Printer) error {
- var pp printer.Printer
+func (e *DuplicateKeyError) GetMessage() string {
+ return e.Message
+}
- var colored, inclSource bool
- if fep, ok := p.(*FormatErrorPrinter); ok {
- colored = fep.Colored
- inclSource = fep.InclSource
- }
+func (e *DuplicateKeyError) GetToken() *token.Token {
+ return e.Token
+}
- pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column)
- msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.msg), colored)
- if inclSource {
- msg += "\n" + pp.PrintErrorToken(e.token, colored)
- }
- p.Print(msg)
+func (e *DuplicateKeyError) Error() string {
+ return e.FormatError(defaultFormatColor, defaultIncludeSource)
+}
- if e.verb == 'v' && e.state.Flag('+') {
- // %+v
- // print stack trace for debugging
- e.frame.Format(p)
- }
- return nil
+func (e *DuplicateKeyError) FormatError(colored, inclSource bool) string {
+ return FormatError(e.Message, e.Token, colored, inclSource)
}
-type PrettyPrinter interface {
- PrettyPrint(xerrors.Printer, bool, bool) error
+func (e *UnknownFieldError) GetMessage() string {
+ return e.Message
}
-type Sink struct{ *bytes.Buffer }
-func (es *Sink) Print(args ...interface{}) {
- fmt.Fprint(es.Buffer, args...)
+func (e *UnknownFieldError) GetToken() *token.Token {
+ return e.Token
}
-func (es *Sink) Printf(f string, args ...interface{}) {
- fmt.Fprintf(es.Buffer, f, args...)
+func (e *UnknownFieldError) Error() string {
+ return e.FormatError(defaultFormatColor, defaultIncludeSource)
}
-func (es *Sink) Detail() bool {
- return false
+func (e *UnknownFieldError) FormatError(colored, inclSource bool) string {
+ return FormatError(e.Message, e.Token, colored, inclSource)
}
-func (e *syntaxError) Error() string {
- var buf bytes.Buffer
- e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource)
- return buf.String()
+func (e *UnexpectedNodeTypeError) GetMessage() string {
+ return e.msg()
}
-type TypeError struct {
- DstType reflect.Type
- SrcType reflect.Type
- StructFieldName *string
- Token *token.Token
+func (e *UnexpectedNodeTypeError) GetToken() *token.Token {
+ return e.Token
}
-func (e *TypeError) Error() string {
- if e.StructFieldName != nil {
- return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType)
- }
- return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType)
+func (e *UnexpectedNodeTypeError) Error() string {
+ return e.FormatError(defaultFormatColor, defaultIncludeSource)
}
-func (e *TypeError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error {
- return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource})
+func (e *UnexpectedNodeTypeError) FormatError(colored, inclSource bool) string {
+ return FormatError(e.msg(), e.Token, colored, inclSource)
}
-func (e *TypeError) FormatError(p xerrors.Printer) error {
- var pp printer.Printer
+func (e *UnexpectedNodeTypeError) msg() string {
+ return fmt.Sprintf("%s was used where %s is expected", e.Actual.YAMLName(), e.Expected.YAMLName())
+}
- var colored, inclSource bool
- if fep, ok := p.(*FormatErrorPrinter); ok {
- colored = fep.Colored
- inclSource = fep.InclSource
+func FormatError(errMsg string, token *token.Token, colored, inclSource bool) string {
+ var pp printer.Printer
+ if token == nil {
+ return pp.PrintErrorMessage(errMsg, colored)
}
-
- pos := fmt.Sprintf("[%d:%d] ", e.Token.Position.Line, e.Token.Position.Column)
- msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored)
+ pos := fmt.Sprintf("[%d:%d] ", token.Position.Line, token.Position.Column)
+ msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, errMsg), colored)
if inclSource {
- msg += "\n" + pp.PrintErrorToken(e.Token, colored)
+ msg += "\n" + pp.PrintErrorToken(token, colored)
}
- p.Print(msg)
-
- return nil
+ return msg
}
diff --git a/vendor/github.com/goccy/go-yaml/internal/format/format.go b/vendor/github.com/goccy/go-yaml/internal/format/format.go
new file mode 100644
index 00000000..2d55652f
--- /dev/null
+++ b/vendor/github.com/goccy/go-yaml/internal/format/format.go
@@ -0,0 +1,539 @@
+package format
+
+import (
+ "strings"
+
+ "github.com/goccy/go-yaml/ast"
+ "github.com/goccy/go-yaml/token"
+)
+
+func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node) string {
+ tk := getFirstToken(n)
+ if tk == nil {
+ return ""
+ }
+ formatter := newFormatter(tk, hasComment(n))
+ formatter.anchorNodeMap = anchorNodeMap
+ return formatter.format(n)
+}
+
+func FormatNode(n ast.Node) string {
+ tk := getFirstToken(n)
+ if tk == nil {
+ return ""
+ }
+ return newFormatter(tk, hasComment(n)).format(n)
+}
+
+func FormatFile(file *ast.File) string {
+ if len(file.Docs) == 0 {
+ return ""
+ }
+ tk := getFirstToken(file.Docs[0])
+ if tk == nil {
+ return ""
+ }
+ return newFormatter(tk, hasCommentFile(file)).formatFile(file)
+}
+
+func hasCommentFile(f *ast.File) bool {
+ for _, doc := range f.Docs {
+ if hasComment(doc.Body) {
+ return true
+ }
+ }
+ return false
+}
+
+func hasComment(n ast.Node) bool {
+ if n == nil {
+ return false
+ }
+ switch nn := n.(type) {
+ case *ast.DocumentNode:
+ return hasComment(nn.Body)
+ case *ast.NullNode:
+ return nn.Comment != nil
+ case *ast.BoolNode:
+ return nn.Comment != nil
+ case *ast.IntegerNode:
+ return nn.Comment != nil
+ case *ast.FloatNode:
+ return nn.Comment != nil
+ case *ast.StringNode:
+ return nn.Comment != nil
+ case *ast.InfinityNode:
+ return nn.Comment != nil
+ case *ast.NanNode:
+ return nn.Comment != nil
+ case *ast.LiteralNode:
+ return nn.Comment != nil
+ case *ast.DirectiveNode:
+ if nn.Comment != nil {
+ return true
+ }
+ for _, value := range nn.Values {
+ if hasComment(value) {
+ return true
+ }
+ }
+ case *ast.TagNode:
+ if nn.Comment != nil {
+ return true
+ }
+ return hasComment(nn.Value)
+ case *ast.MappingNode:
+ if nn.Comment != nil || nn.FootComment != nil {
+ return true
+ }
+ for _, value := range nn.Values {
+ if value.Comment != nil || value.FootComment != nil {
+ return true
+ }
+ if hasComment(value.Key) {
+ return true
+ }
+ if hasComment(value.Value) {
+ return true
+ }
+ }
+ case *ast.MappingKeyNode:
+ return nn.Comment != nil
+ case *ast.MergeKeyNode:
+ return nn.Comment != nil
+ case *ast.SequenceNode:
+ if nn.Comment != nil || nn.FootComment != nil {
+ return true
+ }
+ for _, entry := range nn.Entries {
+ if entry.Comment != nil || entry.HeadComment != nil || entry.LineComment != nil {
+ return true
+ }
+ if hasComment(entry.Value) {
+ return true
+ }
+ }
+ case *ast.AnchorNode:
+ if nn.Comment != nil {
+ return true
+ }
+ if hasComment(nn.Name) || hasComment(nn.Value) {
+ return true
+ }
+ case *ast.AliasNode:
+ if nn.Comment != nil {
+ return true
+ }
+ if hasComment(nn.Value) {
+ return true
+ }
+ }
+ return false
+}
+
+func getFirstToken(n ast.Node) *token.Token {
+ if n == nil {
+ return nil
+ }
+ switch nn := n.(type) {
+ case *ast.DocumentNode:
+ if nn.Start != nil {
+ return nn.Start
+ }
+ return getFirstToken(nn.Body)
+ case *ast.NullNode:
+ return nn.Token
+ case *ast.BoolNode:
+ return nn.Token
+ case *ast.IntegerNode:
+ return nn.Token
+ case *ast.FloatNode:
+ return nn.Token
+ case *ast.StringNode:
+ return nn.Token
+ case *ast.InfinityNode:
+ return nn.Token
+ case *ast.NanNode:
+ return nn.Token
+ case *ast.LiteralNode:
+ return nn.Start
+ case *ast.DirectiveNode:
+ return nn.Start
+ case *ast.TagNode:
+ return nn.Start
+ case *ast.MappingNode:
+ if nn.IsFlowStyle {
+ return nn.Start
+ }
+ if len(nn.Values) == 0 {
+ return nn.Start
+ }
+ return getFirstToken(nn.Values[0].Key)
+ case *ast.MappingKeyNode:
+ return nn.Start
+ case *ast.MergeKeyNode:
+ return nn.Token
+ case *ast.SequenceNode:
+ return nn.Start
+ case *ast.AnchorNode:
+ return nn.Start
+ case *ast.AliasNode:
+ return nn.Start
+ }
+ return nil
+}
+
+type Formatter struct {
+ existsComment bool
+ tokenToOriginMap map[*token.Token]string
+ anchorNodeMap map[string]ast.Node
+}
+
+func newFormatter(tk *token.Token, existsComment bool) *Formatter {
+ tokenToOriginMap := make(map[*token.Token]string)
+ for tk.Prev != nil {
+ tk = tk.Prev
+ }
+ tokenToOriginMap[tk] = tk.Origin
+
+ var origin string
+ for tk.Next != nil {
+ tk = tk.Next
+ if tk.Type == token.CommentType {
+ origin += strings.Repeat("\n", strings.Count(normalizeNewLineChars(tk.Origin), "\n"))
+ continue
+ }
+ origin += tk.Origin
+ tokenToOriginMap[tk] = origin
+ origin = ""
+ }
+ return &Formatter{
+ existsComment: existsComment,
+ tokenToOriginMap: tokenToOriginMap,
+ }
+}
+
+func getIndentNumByFirstLineToken(tk *token.Token) int {
+ defaultIndent := tk.Position.Column - 1
+
+ // key: value
+ // ^
+ // next
+ if tk.Type == token.SequenceEntryType {
+ // If the current token is the sequence entry.
+ // the indent is calculated from the column value of the current token.
+ return defaultIndent
+ }
+
+ // key: value
+ // ^
+ // next
+ if tk.Next != nil && tk.Next.Type == token.MappingValueType {
+ // If the current token is the key in the mapping-value,
+ // the indent is calculated from the column value of the current token.
+ return defaultIndent
+ }
+
+ if tk.Prev == nil {
+ return defaultIndent
+ }
+ prev := tk.Prev
+
+ // key: value
+ // ^
+ // prev
+ if prev.Type == token.MappingValueType {
+ // If the current token is the value in the mapping-value,
+ // the indent is calculated from the column value of the key two steps back.
+ if prev.Prev == nil {
+ return defaultIndent
+ }
+ return prev.Prev.Position.Column - 1
+ }
+
+ // - value
+ // ^
+ // prev
+ if prev.Type == token.SequenceEntryType {
+ // If the value is not a mapping-value and the previous token was a sequence entry,
+ // the indent is calculated using the column value of the sequence entry token.
+ return prev.Position.Column - 1
+ }
+
+ return defaultIndent
+}
+
+func (f *Formatter) format(n ast.Node) string {
+ return f.trimSpacePrefix(
+ f.trimIndentSpace(
+ getIndentNumByFirstLineToken(getFirstToken(n)),
+ f.trimNewLineCharPrefix(f.formatNode(n)),
+ ),
+ )
+}
+
+func (f *Formatter) formatFile(file *ast.File) string {
+ if len(file.Docs) == 0 {
+ return ""
+ }
+ var ret string
+ for _, doc := range file.Docs {
+ ret += f.formatDocument(doc)
+ }
+ return ret
+}
+
+func (f *Formatter) origin(tk *token.Token) string {
+ if tk == nil {
+ return ""
+ }
+ if f.existsComment {
+ return tk.Origin
+ }
+ return f.tokenToOriginMap[tk]
+}
+
+func (f *Formatter) formatDocument(n *ast.DocumentNode) string {
+ return f.origin(n.Start) + f.formatNode(n.Body) + f.origin(n.End)
+}
+
+func (f *Formatter) formatNull(n *ast.NullNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatString(n *ast.StringNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatInteger(n *ast.IntegerNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatFloat(n *ast.FloatNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatBool(n *ast.BoolNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatInfinity(n *ast.InfinityNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatNan(n *ast.NanNode) string {
+ return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatLiteral(n *ast.LiteralNode) string {
+ return f.origin(n.Start) + f.formatCommentGroup(n.Comment) + f.origin(n.Value.Token)
+}
+
+func (f *Formatter) formatMergeKey(n *ast.MergeKeyNode) string {
+ return f.origin(n.Token)
+}
+
+func (f *Formatter) formatMappingValue(n *ast.MappingValueNode) string {
+ return f.formatCommentGroup(n.Comment) +
+ f.origin(n.Key.GetToken()) + ":" + f.formatCommentGroup(n.Key.GetComment()) + f.formatNode(n.Value) +
+ f.formatCommentGroup(n.FootComment)
+}
+
+func (f *Formatter) formatDirective(n *ast.DirectiveNode) string {
+ ret := f.origin(n.Start) + f.formatNode(n.Name)
+ for _, val := range n.Values {
+ ret += f.formatNode(val)
+ }
+ return ret
+}
+
+func (f *Formatter) formatMapping(n *ast.MappingNode) string {
+ var ret string
+ if n.IsFlowStyle {
+ ret = f.origin(n.Start)
+ }
+ ret += f.formatCommentGroup(n.Comment)
+ for _, value := range n.Values {
+ if value.CollectEntry != nil {
+ ret += f.origin(value.CollectEntry)
+ }
+ ret += f.formatMappingValue(value)
+ }
+ if n.IsFlowStyle {
+ ret += f.origin(n.End)
+ }
+ return ret
+}
+
+func (f *Formatter) formatTag(n *ast.TagNode) string {
+ return f.origin(n.Start) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatMappingKey(n *ast.MappingKeyNode) string {
+ return f.origin(n.Start) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
+ var ret string
+ if n.IsFlowStyle {
+ ret = f.origin(n.Start)
+ }
+ if n.Comment != nil {
+ // add head comment.
+ ret += f.formatCommentGroup(n.Comment)
+ }
+ for _, entry := range n.Entries {
+ ret += f.formatNode(entry)
+ }
+ if n.IsFlowStyle {
+ ret += f.origin(n.End)
+ }
+ ret += f.formatCommentGroup(n.FootComment)
+ return ret
+}
+
+func (f *Formatter) formatSequenceEntry(n *ast.SequenceEntryNode) string {
+ return f.formatCommentGroup(n.HeadComment) + f.origin(n.Start) + f.formatCommentGroup(n.LineComment) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatAnchor(n *ast.AnchorNode) string {
+ return f.origin(n.Start) + f.formatNode(n.Name) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatAlias(n *ast.AliasNode) string {
+ if f.anchorNodeMap != nil {
+ anchorName := n.Value.GetToken().Value
+ node := f.anchorNodeMap[anchorName]
+ if node != nil {
+ formatted := f.formatNode(node)
+ // If formatted text contains newline characters, indentation needs to be considered.
+ if strings.Contains(formatted, "\n") {
+ // If the first character is not a newline, the first line should be output without indentation.
+ isIgnoredFirstLine := !strings.HasPrefix(formatted, "\n")
+ formatted = f.addIndentSpace(n.GetToken().Position.IndentNum, formatted, isIgnoredFirstLine)
+ }
+ return formatted
+ }
+ }
+ return f.origin(n.Start) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatNode(n ast.Node) string {
+ switch nn := n.(type) {
+ case *ast.DocumentNode:
+ return f.formatDocument(nn)
+ case *ast.NullNode:
+ return f.formatNull(nn)
+ case *ast.BoolNode:
+ return f.formatBool(nn)
+ case *ast.IntegerNode:
+ return f.formatInteger(nn)
+ case *ast.FloatNode:
+ return f.formatFloat(nn)
+ case *ast.StringNode:
+ return f.formatString(nn)
+ case *ast.InfinityNode:
+ return f.formatInfinity(nn)
+ case *ast.NanNode:
+ return f.formatNan(nn)
+ case *ast.LiteralNode:
+ return f.formatLiteral(nn)
+ case *ast.DirectiveNode:
+ return f.formatDirective(nn)
+ case *ast.TagNode:
+ return f.formatTag(nn)
+ case *ast.MappingNode:
+ return f.formatMapping(nn)
+ case *ast.MappingKeyNode:
+ return f.formatMappingKey(nn)
+ case *ast.MappingValueNode:
+ return f.formatMappingValue(nn)
+ case *ast.MergeKeyNode:
+ return f.formatMergeKey(nn)
+ case *ast.SequenceNode:
+ return f.formatSequence(nn)
+ case *ast.SequenceEntryNode:
+ return f.formatSequenceEntry(nn)
+ case *ast.AnchorNode:
+ return f.formatAnchor(nn)
+ case *ast.AliasNode:
+ return f.formatAlias(nn)
+ }
+ return ""
+}
+
+func (f *Formatter) formatCommentGroup(g *ast.CommentGroupNode) string {
+ if g == nil {
+ return ""
+ }
+ var ret string
+ for _, cm := range g.Comments {
+ ret += f.formatComment(cm)
+ }
+ return ret
+}
+
+func (f *Formatter) formatComment(n *ast.CommentNode) string {
+ if n == nil {
+ return ""
+ }
+ return n.Token.Origin
+}
+
+// nolint: unused
+func (f *Formatter) formatIndent(col int) string {
+ if col <= 1 {
+ return ""
+ }
+ return strings.Repeat(" ", col-1)
+}
+
+func (f *Formatter) trimNewLineCharPrefix(v string) string {
+ return strings.TrimLeftFunc(v, func(r rune) bool {
+ return r == '\n' || r == '\r'
+ })
+}
+
+func (f *Formatter) trimSpacePrefix(v string) string {
+ return strings.TrimLeftFunc(v, func(r rune) bool {
+ return r == ' '
+ })
+}
+
+func (f *Formatter) trimIndentSpace(trimIndentNum int, v string) string {
+ if trimIndentNum == 0 {
+ return v
+ }
+ lines := strings.Split(normalizeNewLineChars(v), "\n")
+ out := make([]string, 0, len(lines))
+ for _, line := range lines {
+ var cnt int
+ out = append(out, strings.TrimLeftFunc(line, func(r rune) bool {
+ cnt++
+ return r == ' ' && cnt <= trimIndentNum
+ }))
+ }
+ return strings.Join(out, "\n")
+}
+
+func (f *Formatter) addIndentSpace(indentNum int, v string, isIgnoredFirstLine bool) string {
+ if indentNum == 0 {
+ return v
+ }
+ indent := strings.Repeat(" ", indentNum)
+ lines := strings.Split(normalizeNewLineChars(v), "\n")
+ out := make([]string, 0, len(lines))
+ for idx, line := range lines {
+ if line == "" || (isIgnoredFirstLine && idx == 0) {
+ out = append(out, line)
+ continue
+ }
+ out = append(out, indent+line)
+ }
+ return strings.Join(out, "\n")
+}
+
+// normalizeNewLineChars normalize CRLF and CR to LF.
+func normalizeNewLineChars(v string) string {
+ return strings.ReplaceAll(strings.ReplaceAll(v, "\r\n", "\n"), "\r", "\n")
+}
diff --git a/vendor/github.com/goccy/go-yaml/option.go b/vendor/github.com/goccy/go-yaml/option.go
index eab5d43a..12b8f27f 100644
--- a/vendor/github.com/goccy/go-yaml/option.go
+++ b/vendor/github.com/goccy/go-yaml/option.go
@@ -1,6 +1,7 @@
package yaml
import (
+ "context"
"io"
"reflect"
@@ -50,11 +51,10 @@ func Validator(v StructValidator) DecodeOption {
}
}
-// Strict enable DisallowUnknownField and DisallowDuplicateKey
+// Strict enable DisallowUnknownField
func Strict() DecodeOption {
return func(d *Decoder) error {
d.disallowUnknownField = true
- d.disallowDuplicateKey = true
return nil
}
}
@@ -69,10 +69,19 @@ func DisallowUnknownField() DecodeOption {
}
}
-// DisallowDuplicateKey causes an error when mapping keys that are duplicates
-func DisallowDuplicateKey() DecodeOption {
+// AllowFieldPrefixes, when paired with [DisallowUnknownField], allows fields
+// with the specified prefixes to bypass the unknown field check.
+func AllowFieldPrefixes(prefixes ...string) DecodeOption {
return func(d *Decoder) error {
- d.disallowDuplicateKey = true
+ d.allowedFieldPrefixes = append(d.allowedFieldPrefixes, prefixes...)
+ return nil
+ }
+}
+
+// AllowDuplicateMapKey ignore syntax error when mapping keys that are duplicates.
+func AllowDuplicateMapKey() DecodeOption {
+ return func(d *Decoder) error {
+ d.allowDuplicateMapKey = true
return nil
}
}
@@ -102,20 +111,32 @@ func UseJSONUnmarshaler() DecodeOption {
func CustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) DecodeOption {
return func(d *Decoder) error {
var typ *T
- d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error {
+ d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error {
return unmarshaler(v.(*T), b)
}
return nil
}
}
+// CustomUnmarshalerContext overrides any decoding process for the type specified in generics.
+// Similar to CustomUnmarshaler, but allows passing a context to the unmarshaler function.
+func CustomUnmarshalerContext[T any](unmarshaler func(context.Context, *T, []byte) error) DecodeOption {
+ return func(d *Decoder) error {
+ var typ *T
+ d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error {
+ return unmarshaler(ctx, v.(*T), b)
+ }
+ return nil
+ }
+}
+
// EncodeOption functional option type for Encoder
type EncodeOption func(e *Encoder) error
// Indent change indent number
func Indent(spaces int) EncodeOption {
return func(e *Encoder) error {
- e.indent = spaces
+ e.indentNum = spaces
return nil
}
}
@@ -144,6 +165,18 @@ func Flow(isFlowStyle bool) EncodeOption {
}
}
+// WithSmartAnchor when multiple map values share the same pointer,
+// an anchor is automatically assigned to the first occurrence, and aliases are used for subsequent elements.
+// The map key name is used as the anchor name by default.
+// If key names conflict, a suffix is automatically added to avoid collisions.
+// This is an experimental feature and cannot be used simultaneously with anchor tags.
+func WithSmartAnchor() EncodeOption {
+ return func(e *Encoder) error {
+ e.enableSmartAnchor = true
+ return nil
+ }
+}
+
// UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax,
// no matter what characters they include
func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption {
@@ -188,13 +221,54 @@ func UseJSONMarshaler() EncodeOption {
func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption {
return func(e *Encoder) error {
var typ T
- e.customMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) {
+ e.customMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) {
return marshaler(v.(T))
}
return nil
}
}
+// CustomMarshalerContext overrides any encoding process for the type specified in generics.
+// Similar to CustomMarshaler, but allows passing a context to the marshaler function.
+func CustomMarshalerContext[T any](marshaler func(context.Context, T) ([]byte, error)) EncodeOption {
+ return func(e *Encoder) error {
+ var typ T
+ e.customMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) {
+ return marshaler(ctx, v.(T))
+ }
+ return nil
+ }
+}
+
+// AutoInt automatically converts floating-point numbers to integers when the fractional part is zero.
+// For example, a value of 1.0 will be encoded as 1.
+func AutoInt() EncodeOption {
+ return func(e *Encoder) error {
+ e.autoInt = true
+ return nil
+ }
+}
+
+// OmitEmpty behaves in the same way as the interpretation of the omitempty tag in the encoding/json library.
+// set on all the fields.
+// In the current implementation, the omitempty tag is not implemented in the same way as encoding/json,
+// so please specify this option if you expect the same behavior.
+func OmitEmpty() EncodeOption {
+ return func(e *Encoder) error {
+ e.omitEmpty = true
+ return nil
+ }
+}
+
+// OmitZero forces the encoder to assume an `omitzero` struct tag is
+// set on all the fields. See `Marshal` commentary for the `omitzero` tag logic.
+func OmitZero() EncodeOption {
+ return func(e *Encoder) error {
+ e.omitZero = true
+ return nil
+ }
+}
+
// CommentPosition type of the position for comment.
type CommentPosition int
diff --git a/vendor/github.com/goccy/go-yaml/parser/color.go b/vendor/github.com/goccy/go-yaml/parser/color.go
new file mode 100644
index 00000000..aeee0dce
--- /dev/null
+++ b/vendor/github.com/goccy/go-yaml/parser/color.go
@@ -0,0 +1,28 @@
+package parser
+
+import "fmt"
+
+const (
+ colorFgHiBlack int = iota + 90
+ colorFgHiRed
+ colorFgHiGreen
+ colorFgHiYellow
+ colorFgHiBlue
+ colorFgHiMagenta
+ colorFgHiCyan
+)
+
+var colorTable = []int{
+ colorFgHiRed,
+ colorFgHiGreen,
+ colorFgHiYellow,
+ colorFgHiBlue,
+ colorFgHiMagenta,
+ colorFgHiCyan,
+}
+
+func colorize(idx int, content string) string {
+ colorIdx := idx % len(colorTable)
+ color := colorTable[colorIdx]
+ return fmt.Sprintf("\x1b[1;%dm", color) + content + "\x1b[22;0m"
+}
diff --git a/vendor/github.com/goccy/go-yaml/parser/context.go b/vendor/github.com/goccy/go-yaml/parser/context.go
index 42cc4f8f..1584b3ec 100644
--- a/vendor/github.com/goccy/go-yaml/parser/context.go
+++ b/vendor/github.com/goccy/go-yaml/parser/context.go
@@ -9,11 +9,15 @@ import (
// context context at parsing
type context struct {
- parent *context
- idx int
+ tokenRef *tokenRef
+ path string
+ isFlow bool
+}
+
+type tokenRef struct {
+ tokens []*Token
size int
- tokens token.Tokens
- path string
+ idx int
}
var pathSpecialChars = []string{
@@ -36,157 +40,148 @@ func normalizePath(path string) string {
return path
}
-func (c *context) withChild(path string) *context {
- ctx := c.copy()
- path = normalizePath(path)
- ctx.path += fmt.Sprintf(".%s", path)
- return ctx
+func (c *context) currentToken() *Token {
+ if c.tokenRef.idx >= c.tokenRef.size {
+ return nil
+ }
+ return c.tokenRef.tokens[c.tokenRef.idx]
}
-func (c *context) withIndex(idx uint) *context {
- ctx := c.copy()
- ctx.path += fmt.Sprintf("[%d]", idx)
- return ctx
+func (c *context) isComment() bool {
+ return c.currentToken().Type() == token.CommentType
}
-func (c *context) copy() *context {
- return &context{
- parent: c,
- idx: c.idx,
- size: c.size,
- tokens: append(token.Tokens{}, c.tokens...),
- path: c.path,
+func (c *context) nextToken() *Token {
+ if c.tokenRef.idx+1 >= c.tokenRef.size {
+ return nil
}
+ return c.tokenRef.tokens[c.tokenRef.idx+1]
}
-func (c *context) next() bool {
- return c.idx < c.size
-}
-
-func (c *context) previousToken() *token.Token {
- if c.idx > 0 {
- return c.tokens[c.idx-1]
+func (c *context) nextNotCommentToken() *Token {
+ for i := c.tokenRef.idx + 1; i < c.tokenRef.size; i++ {
+ tk := c.tokenRef.tokens[i]
+ if tk.Type() == token.CommentType {
+ continue
+ }
+ return tk
}
return nil
}
-func (c *context) insertToken(idx int, tk *token.Token) {
- if c.parent != nil {
- c.parent.insertToken(idx, tk)
- }
- if c.size < idx {
- return
- }
- if c.size == idx {
- curToken := c.tokens[c.size-1]
- tk.Next = curToken
- curToken.Prev = tk
+func (c *context) isTokenNotFound() bool {
+ return c.currentToken() == nil
+}
- c.tokens = append(c.tokens, tk)
- c.size = len(c.tokens)
- return
+func (c *context) withGroup(g *TokenGroup) *context {
+ ctx := *c
+ ctx.tokenRef = &tokenRef{
+ tokens: g.Tokens,
+ size: len(g.Tokens),
}
+ return &ctx
+}
- curToken := c.tokens[idx]
- tk.Next = curToken
- curToken.Prev = tk
+func (c *context) withChild(path string) *context {
+ ctx := *c
+ ctx.path = c.path + "." + normalizePath(path)
+ return &ctx
+}
- c.tokens = append(c.tokens[:idx+1], c.tokens[idx:]...)
- c.tokens[idx] = tk
- c.size = len(c.tokens)
+func (c *context) withIndex(idx uint) *context {
+ ctx := *c
+ ctx.path = c.path + "[" + fmt.Sprint(idx) + "]"
+ return &ctx
}
-func (c *context) currentToken() *token.Token {
- if c.idx >= c.size {
- return nil
- }
- return c.tokens[c.idx]
+func (c *context) withFlow(isFlow bool) *context {
+ ctx := *c
+ ctx.isFlow = isFlow
+ return &ctx
}
-func (c *context) nextToken() *token.Token {
- if c.idx+1 >= c.size {
- return nil
+func newContext() *context {
+ return &context{
+ path: "$",
}
- return c.tokens[c.idx+1]
}
-func (c *context) afterNextToken() *token.Token {
- if c.idx+2 >= c.size {
- return nil
+func (c *context) goNext() {
+ ref := c.tokenRef
+ if ref.size <= ref.idx+1 {
+ ref.idx = ref.size
+ } else {
+ ref.idx++
}
- return c.tokens[c.idx+2]
}
-func (c *context) nextNotCommentToken() *token.Token {
- for i := c.idx + 1; i < c.size; i++ {
- tk := c.tokens[i]
- if tk.Type == token.CommentType {
- continue
- }
- return tk
- }
- return nil
+func (c *context) next() bool {
+ return c.tokenRef.idx < c.tokenRef.size
}
-func (c *context) afterNextNotCommentToken() *token.Token {
- notCommentTokenCount := 0
- for i := c.idx + 1; i < c.size; i++ {
- tk := c.tokens[i]
- if tk.Type == token.CommentType {
- continue
- }
- notCommentTokenCount++
- if notCommentTokenCount == 2 {
- return tk
- }
- }
- return nil
+func (c *context) insertNullToken(tk *Token) *Token {
+ nullToken := c.createImplicitNullToken(tk)
+ c.insertToken(nullToken)
+ c.goNext()
+
+ return nullToken
}
-func (c *context) isCurrentCommentToken() bool {
- tk := c.currentToken()
- if tk == nil {
- return false
- }
- return tk.Type == token.CommentType
+func (c *context) addNullValueToken(tk *Token) *Token {
+ nullToken := c.createImplicitNullToken(tk)
+ rawTk := nullToken.RawToken()
+
+ // add space for map or sequence value.
+ rawTk.Position.Column++
+
+ c.addToken(nullToken)
+ c.goNext()
+
+ return nullToken
}
-func (c *context) progressIgnoreComment(num int) {
- if c.parent != nil {
- c.parent.progressIgnoreComment(num)
- }
- if c.size <= c.idx+num {
- c.idx = c.size
- } else {
- c.idx += num
- }
+func (c *context) createImplicitNullToken(base *Token) *Token {
+ pos := *(base.RawToken().Position)
+ pos.Column++
+ tk := token.New("null", " null", &pos)
+ tk.Type = token.ImplicitNullType
+ return &Token{Token: tk}
}
-func (c *context) progress(num int) {
- if c.isCurrentCommentToken() {
+func (c *context) insertToken(tk *Token) {
+ ref := c.tokenRef
+ idx := ref.idx
+ if ref.size < idx {
return
}
- c.progressIgnoreComment(num)
-}
+ if ref.size == idx {
+ curToken := ref.tokens[ref.size-1]
+ tk.RawToken().Next = curToken.RawToken()
+ curToken.RawToken().Prev = tk.RawToken()
-func newContext(tokens token.Tokens, mode Mode) *context {
- filteredTokens := []*token.Token{}
- if mode&ParseComments != 0 {
- filteredTokens = tokens
- } else {
- for _, tk := range tokens {
- if tk.Type == token.CommentType {
- continue
- }
- // keep prev/next reference between tokens containing comments
- // https://github.com/goccy/go-yaml/issues/254
- filteredTokens = append(filteredTokens, tk)
- }
+ ref.tokens = append(ref.tokens, tk)
+ ref.size = len(ref.tokens)
+ return
}
- return &context{
- idx: 0,
- size: len(filteredTokens),
- tokens: token.Tokens(filteredTokens),
- path: "$",
+
+ curToken := ref.tokens[idx]
+ tk.RawToken().Next = curToken.RawToken()
+ curToken.RawToken().Prev = tk.RawToken()
+
+ ref.tokens = append(ref.tokens[:idx+1], ref.tokens[idx:]...)
+ ref.tokens[idx] = tk
+ ref.size = len(ref.tokens)
+}
+
+func (c *context) addToken(tk *Token) {
+ ref := c.tokenRef
+ lastTk := ref.tokens[ref.size-1]
+ if lastTk.Group != nil {
+ lastTk = lastTk.Group.Last()
}
+ lastTk.RawToken().Next = tk.RawToken()
+ tk.RawToken().Prev = lastTk.RawToken()
+
+ ref.tokens = append(ref.tokens, tk)
+ ref.size = len(ref.tokens)
}
diff --git a/vendor/github.com/goccy/go-yaml/parser/node.go b/vendor/github.com/goccy/go-yaml/parser/node.go
new file mode 100644
index 00000000..8d35554e
--- /dev/null
+++ b/vendor/github.com/goccy/go-yaml/parser/node.go
@@ -0,0 +1,257 @@
+package parser
+
+import (
+ "fmt"
+
+ "github.com/goccy/go-yaml/ast"
+ "github.com/goccy/go-yaml/internal/errors"
+ "github.com/goccy/go-yaml/token"
+)
+
+func newMappingNode(ctx *context, tk *Token, isFlow bool, values ...*ast.MappingValueNode) (*ast.MappingNode, error) {
+ node := ast.Mapping(tk.RawToken(), isFlow, values...)
+ node.SetPath(ctx.path)
+ return node, nil
+}
+
+func newMappingValueNode(ctx *context, colonTk, entryTk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) {
+ node := ast.MappingValue(colonTk.RawToken(), key, value)
+ node.SetPath(ctx.path)
+ node.CollectEntry = entryTk.RawToken()
+ if key.GetToken().Position.Line == value.GetToken().Position.Line {
+ // originally key was commented, but now that null value has been added, value must be commented.
+ if err := setLineComment(ctx, value, colonTk); err != nil {
+ return nil, err
+ }
+ // set line comment by colonTk or entryTk.
+ if err := setLineComment(ctx, value, entryTk); err != nil {
+ return nil, err
+ }
+ } else {
+ if err := setLineComment(ctx, key, colonTk); err != nil {
+ return nil, err
+ }
+ // set line comment by colonTk or entryTk.
+ if err := setLineComment(ctx, key, entryTk); err != nil {
+ return nil, err
+ }
+ }
+ return node, nil
+}
+
+func newMappingKeyNode(ctx *context, tk *Token) (*ast.MappingKeyNode, error) {
+ node := ast.MappingKey(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newAnchorNode(ctx *context, tk *Token) (*ast.AnchorNode, error) {
+ node := ast.Anchor(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newAliasNode(ctx *context, tk *Token) (*ast.AliasNode, error) {
+ node := ast.Alias(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newDirectiveNode(ctx *context, tk *Token) (*ast.DirectiveNode, error) {
+ node := ast.Directive(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newMergeKeyNode(ctx *context, tk *Token) (*ast.MergeKeyNode, error) {
+ node := ast.MergeKey(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newNullNode(ctx *context, tk *Token) (*ast.NullNode, error) {
+ node := ast.Null(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newBoolNode(ctx *context, tk *Token) (*ast.BoolNode, error) {
+ node := ast.Bool(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newIntegerNode(ctx *context, tk *Token) (*ast.IntegerNode, error) {
+ node := ast.Integer(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newFloatNode(ctx *context, tk *Token) (*ast.FloatNode, error) {
+ node := ast.Float(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newInfinityNode(ctx *context, tk *Token) (*ast.InfinityNode, error) {
+ node := ast.Infinity(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newNanNode(ctx *context, tk *Token) (*ast.NanNode, error) {
+ node := ast.Nan(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newStringNode(ctx *context, tk *Token) (*ast.StringNode, error) {
+ node := ast.String(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newLiteralNode(ctx *context, tk *Token) (*ast.LiteralNode, error) {
+ node := ast.Literal(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newTagNode(ctx *context, tk *Token) (*ast.TagNode, error) {
+ node := ast.Tag(tk.RawToken())
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newSequenceNode(ctx *context, tk *Token, isFlow bool) (*ast.SequenceNode, error) {
+ node := ast.Sequence(tk.RawToken(), isFlow)
+ node.SetPath(ctx.path)
+ if err := setLineComment(ctx, node, tk); err != nil {
+ return nil, err
+ }
+ return node, nil
+}
+
+func newTagDefaultScalarValueNode(ctx *context, tag *token.Token) (ast.ScalarNode, error) {
+ pos := *(tag.Position)
+ pos.Column++
+
+ var (
+ tk *Token
+ node ast.ScalarNode
+ )
+ switch token.ReservedTagKeyword(tag.Value) {
+ case token.IntegerTag:
+ tk = &Token{Token: token.New("0", "0", &pos)}
+ n, err := newIntegerNode(ctx, tk)
+ if err != nil {
+ return nil, err
+ }
+ node = n
+ case token.FloatTag:
+ tk = &Token{Token: token.New("0", "0", &pos)}
+ n, err := newFloatNode(ctx, tk)
+ if err != nil {
+ return nil, err
+ }
+ node = n
+ case token.StringTag, token.BinaryTag, token.TimestampTag:
+ tk = &Token{Token: token.New("", "", &pos)}
+ n, err := newStringNode(ctx, tk)
+ if err != nil {
+ return nil, err
+ }
+ node = n
+ case token.BooleanTag:
+ tk = &Token{Token: token.New("false", "false", &pos)}
+ n, err := newBoolNode(ctx, tk)
+ if err != nil {
+ return nil, err
+ }
+ node = n
+ case token.NullTag:
+ tk = &Token{Token: token.New("null", "null", &pos)}
+ n, err := newNullNode(ctx, tk)
+ if err != nil {
+ return nil, err
+ }
+ node = n
+ default:
+ return nil, errors.ErrSyntax(fmt.Sprintf("cannot assign default value for %q tag", tag.Value), tag)
+ }
+ ctx.insertToken(tk)
+ ctx.goNext()
+ return node, nil
+}
+
+func setLineComment(ctx *context, node ast.Node, tk *Token) error {
+ if tk == nil || tk.LineComment == nil {
+ return nil
+ }
+ comment := ast.CommentGroup([]*token.Token{tk.LineComment})
+ comment.SetPath(ctx.path)
+ if err := node.SetComment(comment); err != nil {
+ return err
+ }
+ return nil
+}
+
+func setHeadComment(cm *ast.CommentGroupNode, value ast.Node) error {
+ if cm == nil {
+ return nil
+ }
+ switch n := value.(type) {
+ case *ast.MappingNode:
+ if len(n.Values) != 0 && value.GetComment() == nil {
+ cm.SetPath(n.Values[0].GetPath())
+ return n.Values[0].SetComment(cm)
+ }
+ case *ast.MappingValueNode:
+ cm.SetPath(n.GetPath())
+ return n.SetComment(cm)
+ }
+ cm.SetPath(value.GetPath())
+ return value.SetComment(cm)
+}
diff --git a/vendor/github.com/goccy/go-yaml/parser/option.go b/vendor/github.com/goccy/go-yaml/parser/option.go
new file mode 100644
index 00000000..3121a64a
--- /dev/null
+++ b/vendor/github.com/goccy/go-yaml/parser/option.go
@@ -0,0 +1,12 @@
+package parser
+
+// Option represents parser's option.
+type Option func(p *parser)
+
+// AllowDuplicateMapKey allow the use of keys with the same name in the same map,
+// but by default, this is not permitted.
+func AllowDuplicateMapKey() Option {
+ return func(p *parser) {
+ p.allowDuplicateMapKey = true
+ }
+}
diff --git a/vendor/github.com/goccy/go-yaml/parser/parser.go b/vendor/github.com/goccy/go-yaml/parser/parser.go
index 2bec5fea..2c79d369 100644
--- a/vendor/github.com/goccy/go-yaml/parser/parser.go
+++ b/vendor/github.com/goccy/go-yaml/parser/parser.go
@@ -9,735 +9,1312 @@ import (
"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/lexer"
"github.com/goccy/go-yaml/token"
- "golang.org/x/xerrors"
)
-type parser struct{}
+type Mode uint
-func (p *parser) parseMapping(ctx *context) (*ast.MappingNode, error) {
- mapTk := ctx.currentToken()
- node := ast.Mapping(mapTk, true)
- node.SetPath(ctx.path)
- ctx.progress(1) // skip MappingStart token
- for ctx.next() {
- tk := ctx.currentToken()
- if tk.Type == token.MappingEndType {
- node.End = tk
- return node, nil
- } else if tk.Type == token.CollectEntryType {
- ctx.progress(1)
- continue
+const (
+ ParseComments Mode = 1 << iota // parse comments and add them to AST
+)
+
+// ParseBytes parse from byte slice, and returns ast.File
+func ParseBytes(bytes []byte, mode Mode, opts ...Option) (*ast.File, error) {
+ tokens := lexer.Tokenize(string(bytes))
+ f, err := Parse(tokens, mode, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+// Parse parse from token instances, and returns ast.File
+func Parse(tokens token.Tokens, mode Mode, opts ...Option) (*ast.File, error) {
+ if tk := tokens.InvalidToken(); tk != nil {
+ return nil, errors.ErrSyntax(tk.Error, tk)
+ }
+ p, err := newParser(tokens, mode, opts)
+ if err != nil {
+ return nil, err
+ }
+ f, err := p.parse(newContext())
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+// Parse parse from filename, and returns ast.File
+func ParseFile(filename string, mode Mode, opts ...Option) (*ast.File, error) {
+ file, err := os.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ f, err := ParseBytes(file, mode, opts...)
+ if err != nil {
+ return nil, err
+ }
+ f.Name = filename
+ return f, nil
+}
+
+type YAMLVersion string
+
+const (
+ YAML10 YAMLVersion = "1.0"
+ YAML11 YAMLVersion = "1.1"
+ YAML12 YAMLVersion = "1.2"
+ YAML13 YAMLVersion = "1.3"
+)
+
+var yamlVersionMap = map[string]YAMLVersion{
+ "1.0": YAML10,
+ "1.1": YAML11,
+ "1.2": YAML12,
+ "1.3": YAML13,
+}
+
+type parser struct {
+ tokens []*Token
+ pathMap map[string]ast.Node
+ yamlVersion YAMLVersion
+ allowDuplicateMapKey bool
+ secondaryTagDirective *ast.DirectiveNode
+}
+
+func newParser(tokens token.Tokens, mode Mode, opts []Option) (*parser, error) {
+ filteredTokens := []*token.Token{}
+ if mode&ParseComments != 0 {
+ filteredTokens = tokens
+ } else {
+ for _, tk := range tokens {
+ if tk.Type == token.CommentType {
+ continue
+ }
+ // keep prev/next reference between tokens containing comments
+ // https://github.com/goccy/go-yaml/issues/254
+ filteredTokens = append(filteredTokens, tk)
+ }
+ }
+ tks, err := CreateGroupedTokens(token.Tokens(filteredTokens))
+ if err != nil {
+ return nil, err
+ }
+ p := &parser{
+ tokens: tks,
+ pathMap: make(map[string]ast.Node),
+ }
+ for _, opt := range opts {
+ opt(p)
+ }
+ return p, nil
+}
+
+func (p *parser) parse(ctx *context) (*ast.File, error) {
+ file := &ast.File{Docs: []*ast.DocumentNode{}}
+ for _, token := range p.tokens {
+ doc, err := p.parseDocument(ctx, token.Group)
+ if err != nil {
+ return nil, err
}
+ file.Docs = append(file.Docs, doc)
+ }
+ return file, nil
+}
+
+func (p *parser) parseDocument(ctx *context, docGroup *TokenGroup) (*ast.DocumentNode, error) {
+ if len(docGroup.Tokens) == 0 {
+ return ast.Document(docGroup.RawToken(), nil), nil
+ }
+
+ p.pathMap = make(map[string]ast.Node)
+
+ var (
+ tokens = docGroup.Tokens
+ start *token.Token
+ end *token.Token
+ )
+ if docGroup.First().Type() == token.DocumentHeaderType {
+ start = docGroup.First().RawToken()
+ tokens = tokens[1:]
+ }
+ if docGroup.Last().Type() == token.DocumentEndType {
+ end = docGroup.Last().RawToken()
+ tokens = tokens[:len(tokens)-1]
+ defer func() {
+ // clear yaml version value if DocumentEnd token (...) is specified.
+ p.yamlVersion = ""
+ }()
+ }
+
+ if len(tokens) == 0 {
+ return ast.Document(docGroup.RawToken(), nil), nil
+ }
+
+ body, err := p.parseDocumentBody(ctx.withGroup(&TokenGroup{
+ Type: TokenGroupDocumentBody,
+ Tokens: tokens,
+ }))
+ if err != nil {
+ return nil, err
+ }
+ node := ast.Document(start, body)
+ node.End = end
+ return node, nil
+}
+
+func (p *parser) parseDocumentBody(ctx *context) (ast.Node, error) {
+ node, err := p.parseToken(ctx, ctx.currentToken())
+ if err != nil {
+ return nil, err
+ }
+ if ctx.next() {
+ return nil, errors.ErrSyntax("value is not allowed in this context", ctx.currentToken().RawToken())
+ }
+ return node, nil
+}
- value, err := p.parseMappingValue(ctx)
+func (p *parser) parseToken(ctx *context, tk *Token) (ast.Node, error) {
+ switch tk.GroupType() {
+ case TokenGroupMapKey, TokenGroupMapKeyValue:
+ return p.parseMap(ctx)
+ case TokenGroupDirective:
+ node, err := p.parseDirective(ctx.withGroup(tk.Group), tk.Group)
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ return node, nil
+ case TokenGroupDirectiveName:
+ node, err := p.parseDirectiveName(ctx.withGroup(tk.Group))
if err != nil {
- return nil, errors.Wrapf(err, "failed to parse mapping value in mapping node")
+ return nil, err
}
- mvnode, ok := value.(*ast.MappingValueNode)
- if !ok {
- return nil, errors.ErrSyntax("failed to parse flow mapping node", value.GetToken())
+ ctx.goNext()
+ return node, nil
+ case TokenGroupAnchor:
+ node, err := p.parseAnchor(ctx.withGroup(tk.Group), tk.Group)
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ return node, nil
+ case TokenGroupAnchorName:
+ anchor, err := p.parseAnchorName(ctx.withGroup(tk.Group))
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ if ctx.isTokenNotFound() {
+ return nil, errors.ErrSyntax("could not find anchor value", tk.RawToken())
+ }
+ value, err := p.parseToken(ctx, ctx.currentToken())
+ if err != nil {
+ return nil, err
+ }
+ if _, ok := value.(*ast.AnchorNode); ok {
+ return nil, errors.ErrSyntax("anchors cannot be used consecutively", value.GetToken())
+ }
+ anchor.Value = value
+ return anchor, nil
+ case TokenGroupAlias:
+ node, err := p.parseAlias(ctx.withGroup(tk.Group))
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ return node, nil
+ case TokenGroupLiteral, TokenGroupFolded:
+ node, err := p.parseLiteral(ctx.withGroup(tk.Group))
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ return node, nil
+ case TokenGroupScalarTag:
+ node, err := p.parseTag(ctx.withGroup(tk.Group))
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ return node, nil
+ }
+ switch tk.Type() {
+ case token.CommentType:
+ return p.parseComment(ctx)
+ case token.TagType:
+ return p.parseTag(ctx)
+ case token.MappingStartType:
+ return p.parseFlowMap(ctx.withFlow(true))
+ case token.SequenceStartType:
+ return p.parseFlowSequence(ctx.withFlow(true))
+ case token.SequenceEntryType:
+ return p.parseSequence(ctx)
+ case token.SequenceEndType:
+ // SequenceEndType is always validated in parseFlowSequence.
+ // Therefore, if this is found in other cases, it is treated as a syntax error.
+ return nil, errors.ErrSyntax("could not find '[' character corresponding to ']'", tk.RawToken())
+ case token.MappingEndType:
+ // MappingEndType is always validated in parseFlowMap.
+ // Therefore, if this is found in other cases, it is treated as a syntax error.
+ return nil, errors.ErrSyntax("could not find '{' character corresponding to '}'", tk.RawToken())
+ case token.MappingValueType:
+ return nil, errors.ErrSyntax("found an invalid key for this map", tk.RawToken())
+ }
+ node, err := p.parseScalarValue(ctx, tk)
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ return node, nil
+}
+
+func (p *parser) parseScalarValue(ctx *context, tk *Token) (ast.ScalarNode, error) {
+ if tk.Group != nil {
+ switch tk.GroupType() {
+ case TokenGroupAnchor:
+ return p.parseAnchor(ctx.withGroup(tk.Group), tk.Group)
+ case TokenGroupAnchorName:
+ anchor, err := p.parseAnchorName(ctx.withGroup(tk.Group))
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+ if ctx.isTokenNotFound() {
+ return nil, errors.ErrSyntax("could not find anchor value", tk.RawToken())
+ }
+ value, err := p.parseToken(ctx, ctx.currentToken())
+ if err != nil {
+ return nil, err
+ }
+ if _, ok := value.(*ast.AnchorNode); ok {
+ return nil, errors.ErrSyntax("anchors cannot be used consecutively", value.GetToken())
+ }
+ anchor.Value = value
+ return anchor, nil
+ case TokenGroupAlias:
+ return p.parseAlias(ctx.withGroup(tk.Group))
+ case TokenGroupLiteral, TokenGroupFolded:
+ return p.parseLiteral(ctx.withGroup(tk.Group))
+ case TokenGroupScalarTag:
+ return p.parseTag(ctx.withGroup(tk.Group))
+ default:
+ return nil, errors.ErrSyntax("unexpected scalar value", tk.RawToken())
}
- node.Values = append(node.Values, mvnode)
- ctx.progress(1)
}
- return nil, errors.ErrSyntax("unterminated flow mapping", node.GetToken())
+ switch tk.Type() {
+ case token.MergeKeyType:
+ return newMergeKeyNode(ctx, tk)
+ case token.NullType, token.ImplicitNullType:
+ return newNullNode(ctx, tk)
+ case token.BoolType:
+ return newBoolNode(ctx, tk)
+ case token.IntegerType, token.BinaryIntegerType, token.OctetIntegerType, token.HexIntegerType:
+ return newIntegerNode(ctx, tk)
+ case token.FloatType:
+ return newFloatNode(ctx, tk)
+ case token.InfinityType:
+ return newInfinityNode(ctx, tk)
+ case token.NanType:
+ return newNanNode(ctx, tk)
+ case token.StringType, token.SingleQuoteType, token.DoubleQuoteType:
+ return newStringNode(ctx, tk)
+ case token.TagType:
+ // this case applies when it is a scalar tag and its value does not exist.
+ // Examples of cases where the value does not exist include cases like `key: !!str,` or `!!str : value`.
+ return p.parseScalarTag(ctx)
+ }
+ return nil, errors.ErrSyntax("unexpected scalar value type", tk.RawToken())
}
-func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) {
- node := ast.Sequence(ctx.currentToken(), true)
- node.SetPath(ctx.path)
- ctx.progress(1) // skip SequenceStart token
+func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
+ node, err := newMappingNode(ctx, ctx.currentToken(), true)
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext() // skip MappingStart token
+
+ isFirst := true
for ctx.next() {
tk := ctx.currentToken()
- if tk.Type == token.SequenceEndType {
- node.End = tk
+ if tk.Type() == token.MappingEndType {
+ node.End = tk.RawToken()
break
- } else if tk.Type == token.CollectEntryType {
- ctx.progress(1)
- continue
}
- value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), tk)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse sequence value in flow sequence node")
+ var entryTk *Token
+ if tk.Type() == token.CollectEntryType {
+ entryTk = tk
+ ctx.goNext()
+ } else if !isFirst {
+ return nil, errors.ErrSyntax("',' or '}' must be specified", tk.RawToken())
}
- node.Values = append(node.Values, value)
- ctx.progress(1)
+
+ if tk := ctx.currentToken(); tk.Type() == token.MappingEndType {
+ // this case is here: "{ elem, }".
+ // In this case, ignore the last element and break mapping parsing.
+ node.End = tk.RawToken()
+ break
+ }
+
+ mapKeyTk := ctx.currentToken()
+ switch mapKeyTk.GroupType() {
+ case TokenGroupMapKeyValue:
+ value, err := p.parseMapKeyValue(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group, entryTk)
+ if err != nil {
+ return nil, err
+ }
+ node.Values = append(node.Values, value)
+ ctx.goNext()
+ case TokenGroupMapKey:
+ key, err := p.parseMapKey(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group)
+ if err != nil {
+ return nil, err
+ }
+ ctx := ctx.withChild(p.mapKeyText(key))
+ colonTk := mapKeyTk.Group.Last()
+ if p.isFlowMapDelim(ctx.nextToken()) {
+ value, err := newNullNode(ctx, ctx.insertNullToken(colonTk))
+ if err != nil {
+ return nil, err
+ }
+ mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value)
+ if err != nil {
+ return nil, err
+ }
+ node.Values = append(node.Values, mapValue)
+ ctx.goNext()
+ } else {
+ ctx.goNext()
+ if ctx.isTokenNotFound() {
+ return nil, errors.ErrSyntax("could not find map value", colonTk.RawToken())
+ }
+ value, err := p.parseToken(ctx, ctx.currentToken())
+ if err != nil {
+ return nil, err
+ }
+ mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value)
+ if err != nil {
+ return nil, err
+ }
+ node.Values = append(node.Values, mapValue)
+ }
+ default:
+ if !p.isFlowMapDelim(ctx.nextToken()) {
+ errTk := mapKeyTk
+ if errTk == nil {
+ errTk = tk
+ }
+ return nil, errors.ErrSyntax("could not find flow map content", errTk.RawToken())
+ }
+ key, err := p.parseScalarValue(ctx, mapKeyTk)
+ if err != nil {
+ return nil, err
+ }
+ value, err := newNullNode(ctx, ctx.insertNullToken(mapKeyTk))
+ if err != nil {
+ return nil, err
+ }
+ mapValue, err := newMappingValueNode(ctx, mapKeyTk, entryTk, key, value)
+ if err != nil {
+ return nil, err
+ }
+ node.Values = append(node.Values, mapValue)
+ ctx.goNext()
+ }
+ isFirst = false
}
+ if node.End == nil {
+ return nil, errors.ErrSyntax("could not find flow mapping end token '}'", node.Start)
+ }
+ ctx.goNext() // skip mapping end token.
return node, nil
}
-func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) {
- tagToken := ctx.currentToken()
- node := ast.Tag(tagToken)
- node.SetPath(ctx.path)
- ctx.progress(1) // skip tag token
- var (
- value ast.Node
- err error
- )
- switch token.ReservedTagKeyword(tagToken.Value) {
- case token.MappingTag,
- token.OrderedMapTag:
- value, err = p.parseMapping(ctx)
- case token.IntegerTag,
- token.FloatTag,
- token.StringTag,
- token.BinaryTag,
- token.TimestampTag,
- token.NullTag:
- typ := ctx.currentToken().Type
- if typ == token.LiteralType || typ == token.FoldedType {
- value, err = p.parseLiteral(ctx)
- } else {
- value = p.parseScalarValue(ctx.currentToken())
+func (p *parser) isFlowMapDelim(tk *Token) bool {
+ return tk.Type() == token.MappingEndType || tk.Type() == token.CollectEntryType
+}
+
+func (p *parser) parseMap(ctx *context) (*ast.MappingNode, error) {
+ keyTk := ctx.currentToken()
+ if keyTk.Group == nil {
+ return nil, errors.ErrSyntax("unexpected map key", keyTk.RawToken())
+ }
+ var keyValueNode *ast.MappingValueNode
+ if keyTk.GroupType() == TokenGroupMapKeyValue {
+ node, err := p.parseMapKeyValue(ctx.withGroup(keyTk.Group), keyTk.Group, nil)
+ if err != nil {
+ return nil, err
}
- case token.SequenceTag,
- token.SetTag:
- err = errors.ErrSyntax(fmt.Sprintf("sorry, currently not supported %s tag", tagToken.Value), tagToken)
- default:
- // custom tag
- value, err = p.parseToken(ctx, ctx.currentToken())
+ keyValueNode = node
+ ctx.goNext()
+ if err := p.validateMapKeyValueNextToken(ctx, keyTk, ctx.currentToken()); err != nil {
+ return nil, err
+ }
+ } else {
+ key, err := p.parseMapKey(ctx.withGroup(keyTk.Group), keyTk.Group)
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext()
+
+ valueTk := ctx.currentToken()
+ if keyTk.Line() == valueTk.Line() && valueTk.Type() == token.SequenceEntryType {
+ return nil, errors.ErrSyntax("block sequence entries are not allowed in this context", valueTk.RawToken())
+ }
+ ctx := ctx.withChild(p.mapKeyText(key))
+ value, err := p.parseMapValue(ctx, key, keyTk.Group.Last())
+ if err != nil {
+ return nil, err
+ }
+ node, err := newMappingValueNode(ctx, keyTk.Group.Last(), nil, key, value)
+ if err != nil {
+ return nil, err
+ }
+ keyValueNode = node
}
+ mapNode, err := newMappingNode(ctx, &Token{Token: keyValueNode.GetToken()}, false, keyValueNode)
if err != nil {
- return nil, errors.Wrapf(err, "failed to parse tag value")
+ return nil, err
}
- node.Value = value
- return node, nil
+ var tk *Token
+ if ctx.isComment() {
+ tk = ctx.nextNotCommentToken()
+ } else {
+ tk = ctx.currentToken()
+ }
+ for tk.Column() == keyTk.Column() {
+ typ := tk.Type()
+ if ctx.isFlow && typ == token.SequenceEndType {
+ // [
+ // key: value
+ // ] <=
+ break
+ }
+ if !p.isMapToken(tk) {
+ return nil, errors.ErrSyntax("non-map value is specified", tk.RawToken())
+ }
+ cm := p.parseHeadComment(ctx)
+ if typ == token.MappingEndType {
+ // a: {
+ // b: c
+ // } <=
+ ctx.goNext()
+ break
+ }
+ node, err := p.parseMap(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if len(node.Values) != 0 {
+ if err := setHeadComment(cm, node.Values[0]); err != nil {
+ return nil, err
+ }
+ }
+ mapNode.Values = append(mapNode.Values, node.Values...)
+ if node.FootComment != nil {
+ mapNode.Values[len(mapNode.Values)-1].FootComment = node.FootComment
+ }
+ tk = ctx.currentToken()
+ }
+ if ctx.isComment() {
+ if keyTk.Column() <= ctx.currentToken().Column() {
+ // If the comment is in the same or deeper column as the last element column in map value,
+ // treat it as a footer comment for the last element.
+ if len(mapNode.Values) == 1 {
+ mapNode.Values[0].FootComment = p.parseFootComment(ctx, keyTk.Column())
+ mapNode.Values[0].FootComment.SetPath(mapNode.Values[0].Key.GetPath())
+ } else {
+ mapNode.FootComment = p.parseFootComment(ctx, keyTk.Column())
+ mapNode.FootComment.SetPath(mapNode.GetPath())
+ }
+ }
+ }
+ return mapNode, nil
}
-func (p *parser) removeLeftSideNewLineCharacter(src string) string {
- // CR or LF or CRLF
- return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n")
+func (p *parser) validateMapKeyValueNextToken(ctx *context, keyTk, tk *Token) error {
+ if tk == nil {
+ return nil
+ }
+ if tk.Column() <= keyTk.Column() {
+ return nil
+ }
+ if ctx.isComment() {
+ return nil
+ }
+ if ctx.isFlow && (tk.Type() == token.CollectEntryType || tk.Type() == token.SequenceEndType) {
+ return nil
+ }
+ // a: b
+ // c <= this token is invalid.
+ return errors.ErrSyntax("value is not allowed in this context. map key-value is pre-defined", tk.RawToken())
}
-func (p *parser) existsNewLineCharacter(src string) bool {
- if strings.Index(src, "\n") > 0 {
- return true
+func (p *parser) isMapToken(tk *Token) bool {
+ if tk.Group == nil {
+ return tk.Type() == token.MappingStartType || tk.Type() == token.MappingEndType
+ }
+ g := tk.Group
+ return g.Type == TokenGroupMapKey || g.Type == TokenGroupMapKeyValue
+}
+
+func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup, entryTk *Token) (*ast.MappingValueNode, error) {
+ if g.Type != TokenGroupMapKeyValue {
+ return nil, errors.ErrSyntax("unexpected map key-value pair", g.RawToken())
+ }
+ if g.First().Group == nil {
+ return nil, errors.ErrSyntax("unexpected map key", g.RawToken())
}
- if strings.Index(src, "\r") > 0 {
- return true
+ keyGroup := g.First().Group
+ key, err := p.parseMapKey(ctx.withGroup(keyGroup), keyGroup)
+ if err != nil {
+ return nil, err
}
- return false
+
+ c := ctx.withChild(p.mapKeyText(key))
+ value, err := p.parseToken(c, g.Last())
+ if err != nil {
+ return nil, err
+ }
+ return newMappingValueNode(c, keyGroup.Last(), entryTk, key, value)
}
-func (p *parser) validateMapKey(tk *token.Token) error {
- if tk.Type != token.StringType {
+func (p *parser) parseMapKey(ctx *context, g *TokenGroup) (ast.MapKeyNode, error) {
+ if g.Type != TokenGroupMapKey {
+ return nil, errors.ErrSyntax("unexpected map key", g.RawToken())
+ }
+ if g.First().Type() == token.MappingKeyType {
+ mapKeyTk := g.First()
+ if mapKeyTk.Group != nil {
+ ctx = ctx.withGroup(mapKeyTk.Group)
+ }
+ key, err := newMappingKeyNode(ctx, mapKeyTk)
+ if err != nil {
+ return nil, err
+ }
+ ctx.goNext() // skip mapping key token
+ if ctx.isTokenNotFound() {
+ return nil, errors.ErrSyntax("could not find value for mapping key", mapKeyTk.RawToken())
+ }
+
+ scalar, err := p.parseScalarValue(ctx, ctx.currentToken())
+ if err != nil {
+ return nil, err
+ }
+ key.Value = scalar
+ keyText := p.mapKeyText(scalar)
+ keyPath := ctx.withChild(keyText).path
+ key.SetPath(keyPath)
+ if err := p.validateMapKey(ctx, key.GetToken(), keyPath, g.Last()); err != nil {
+ return nil, err
+ }
+ p.pathMap[keyPath] = key
+ return key, nil
+ }
+ if g.Last().Type() != token.MappingValueType {
+ return nil, errors.ErrSyntax("expected map key-value delimiter ':'", g.Last().RawToken())
+ }
+
+ scalar, err := p.parseScalarValue(ctx, g.First())
+ if err != nil {
+ return nil, err
+ }
+ key, ok := scalar.(ast.MapKeyNode)
+ if !ok {
+ return nil, errors.ErrSyntax("cannot take map-key node", scalar.GetToken())
+ }
+ keyText := p.mapKeyText(key)
+ keyPath := ctx.withChild(keyText).path
+ key.SetPath(keyPath)
+ if err := p.validateMapKey(ctx, key.GetToken(), keyPath, g.Last()); err != nil {
+ return nil, err
+ }
+ p.pathMap[keyPath] = key
+ return key, nil
+}
+
+func (p *parser) validateMapKey(ctx *context, tk *token.Token, keyPath string, colonTk *Token) error {
+ if !p.allowDuplicateMapKey {
+ if n, exists := p.pathMap[keyPath]; exists {
+ pos := n.GetToken().Position
+ return errors.ErrSyntax(
+ fmt.Sprintf("mapping key %q already defined at [%d:%d]", tk.Value, pos.Line, pos.Column),
+ tk,
+ )
+ }
+ }
+ origin := p.removeLeftWhiteSpace(tk.Origin)
+ if ctx.isFlow {
+ if tk.Type == token.StringType {
+ origin = p.removeRightWhiteSpace(origin)
+ if tk.Position.Line+p.newLineCharacterNum(origin) != colonTk.Line() {
+ return errors.ErrSyntax("map key definition includes an implicit line break", tk)
+ }
+ }
+ return nil
+ }
+ if tk.Type != token.StringType && tk.Type != token.SingleQuoteType && tk.Type != token.DoubleQuoteType {
return nil
}
- origin := p.removeLeftSideNewLineCharacter(tk.Origin)
if p.existsNewLineCharacter(origin) {
return errors.ErrSyntax("unexpected key name", tk)
}
return nil
}
-func (p *parser) createNullToken(base *token.Token) *token.Token {
- pos := *(base.Position)
- pos.Column++
- return token.New("null", "null", &pos)
+func (p *parser) removeLeftWhiteSpace(src string) string {
+ // CR or LF or CRLF
+ return strings.TrimLeftFunc(src, func(r rune) bool {
+ return r == ' ' || r == '\r' || r == '\n'
+ })
}
-func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) {
- node, err := p.createMapValueNode(ctx, key, colonToken)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to create map value node")
- }
- if node != nil && node.GetPath() == "" {
- node.SetPath(ctx.path)
+func (p *parser) removeRightWhiteSpace(src string) string {
+ // CR or LF or CRLF
+ return strings.TrimRightFunc(src, func(r rune) bool {
+ return r == ' ' || r == '\r' || r == '\n'
+ })
+}
+
+func (p *parser) existsNewLineCharacter(src string) bool {
+ return p.newLineCharacterNum(src) > 0
+}
+
+func (p *parser) newLineCharacterNum(src string) int {
+ var num int
+ for i := 0; i < len(src); i++ {
+ switch src[i] {
+ case '\r':
+ if len(src) > i+1 && src[i+1] == '\n' {
+ i++
+ }
+ num++
+ case '\n':
+ num++
+ }
}
- return node, nil
+ return num
}
-func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) {
+func (p *parser) mapKeyText(n ast.Node) string {
+ if n == nil {
+ return ""
+ }
+ switch nn := n.(type) {
+ case *ast.MappingKeyNode:
+ return p.mapKeyText(nn.Value)
+ case *ast.TagNode:
+ return p.mapKeyText(nn.Value)
+ case *ast.AnchorNode:
+ return p.mapKeyText(nn.Value)
+ case *ast.AliasNode:
+ return ""
+ }
+ return n.GetToken().Value
+}
+
+func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonTk *Token) (ast.Node, error) {
tk := ctx.currentToken()
if tk == nil {
- nullToken := p.createNullToken(colonToken)
- ctx.insertToken(ctx.idx, nullToken)
- return ast.Null(nullToken), nil
+ return newNullNode(ctx, ctx.addNullValueToken(colonTk))
}
- var comment *ast.CommentGroupNode
- if tk.Type == token.CommentType {
- comment = p.parseCommentOnly(ctx)
- if comment != nil {
- comment.SetPath(ctx.withChild(key.GetToken().Value).path)
- }
- tk = ctx.currentToken()
+
+ if ctx.isComment() {
+ tk = ctx.nextNotCommentToken()
}
- if tk.Position.Column == key.GetToken().Position.Column && tk.Type == token.StringType {
+ keyCol := key.GetToken().Position.Column
+ keyLine := key.GetToken().Position.Line
+
+ if tk.Column() != keyCol && tk.Line() == keyLine && (tk.GroupType() == TokenGroupMapKey || tk.GroupType() == TokenGroupMapKeyValue) {
+ // a: b:
+ // ^
+ //
+ // a: b: c
+ // ^
+ return nil, errors.ErrSyntax("mapping value is not allowed in this context", tk.RawToken())
+ }
+
+ if tk.Column() == keyCol && p.isMapToken(tk) {
// in this case,
// ----
// key: