Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: goccy/go-yaml
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.11.3
Choose a base ref
...
head repository: goccy/go-yaml
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.12.0
Choose a head ref
  • 8 commits
  • 12 files changed
  • 9 contributors

Commits on Feb 26, 2024

  1. Replace deprecated io/ioutil (#429)

    harryzcy authored Feb 26, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    renovate-bot Mend Renovate
    Copy the full SHA
    4653a1b View commit details

Commits on Jun 19, 2024

  1. support custom unmarshalling for map keys (#453)

    Co-authored-by: k.safin@npo-echelon.ru <k.safin@npo-echelon.ru>
    KSpaceer and k.safin@npo-echelon.ru authored Jun 19, 2024
    Copy the full SHA
    7ef5c62 View commit details
  2. Update go.yml

    goccy authored Jun 19, 2024
    Copy the full SHA
    a67648e View commit details
  3. Update go.yml

    goccy authored Jun 19, 2024
    Copy the full SHA
    b2a8cc6 View commit details

Commits on Jul 16, 2024

  1. trim right spaces before adding carriage return or linefeed (#462)

    Signed-off-by: Matthew F Leader <mleader@redhat.com>
    Co-authored-by: Webb Scales <wscales@redhat.com>
    mfleader and webbnh authored Jul 16, 2024
    Copy the full SHA
    56a6b54 View commit details
  2. fix: Correct token.Tokenize double quoted strings with escape sequenc…

    …es handling (#457)
    
    * fix scan double quote index progression
    
    * fix: Correct root path handling (#2)
    
    Ref: #456
    nieomylnieja authored Jul 16, 2024
    Copy the full SHA
    1f84c0c View commit details
  3. Fix decoding of scientific notation (#463)

    * Fix scientific notation decoding and add encoding test cases
    
    * Deal with ints and uints
    
    * Add coverage for uint changes
    morris-kelly authored Jul 16, 2024
    Copy the full SHA
    b5f63d5 View commit details
  4. Quote is required even if it begins with backquote. (#440)

    k1LoW authored Jul 16, 2024
    Copy the full SHA
    237df0e View commit details
Showing with 2,155 additions and 169 deletions.
  1. +2 −1 .github/workflows/go.yml
  2. +1 −2 cmd/ycat/ycat.go
  3. +49 −6 decode.go
  4. +122 −1 decode_test.go
  5. +25 −0 encode_test.go
  6. +1,827 −53 lexer/lexer_test.go
  7. +2 −2 parser/parser.go
  8. +3 −0 parser/parser_test.go
  9. +1 −1 path.go
  10. +25 −0 path_test.go
  11. +97 −102 scanner/scanner.go
  12. +1 −1 token/token.go
3 changes: 2 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -73,7 +73,8 @@ jobs:
- name: measure coverage
run: |
make cover
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
verbose: true
token: ${{ secrets.CODECOV_TOKEN }}
3 changes: 1 addition & 2 deletions cmd/ycat/ycat.go
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ package main
import (
"errors"
"fmt"
"io/ioutil"
"os"

"github.com/fatih/color"
@@ -24,7 +23,7 @@ func _main(args []string) error {
return errors.New("ycat: usage: ycat file.yml")
}
filename := args[1]
bytes, err := ioutil.ReadFile(filename)
bytes, err := os.ReadFile(filename)
if err != nil {
return err
}
55 changes: 49 additions & 6 deletions decode.go
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
@@ -16,11 +15,12 @@ import (
"strconv"
"time"

"golang.org/x/xerrors"

"github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/parser"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
)

// Decoder reads and decodes YAML values from an input stream.
@@ -488,6 +488,21 @@ func (d *Decoder) fileToNode(f *ast.File) ast.Node {
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) {

// Special case for "strings -> floats" aka scientific notation
// If the destination type is a float and the source type is a string, check if we can
// use strconv.ParseFloat to convert the string to a float.
if (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) &&
v.Type().Kind() == reflect.String {
if f, err := strconv.ParseFloat(v.String(), 64); err == nil {
if typ.Kind() == reflect.Float32 {
return reflect.ValueOf(float32(f)), nil
} else if typ.Kind() == reflect.Float64 {
return reflect.ValueOf(f), nil
}
// else, fall through to the error below
}
}
return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken())
}
return v.Convert(typ), nil
@@ -877,6 +892,15 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
dst.SetInt(int64(vv))
return nil
}
case string: // handle scientific notation
if i, err := strconv.ParseFloat(vv, 64); err == nil {
if 0 <= i && i <= math.MaxUint64 && !dst.OverflowInt(int64(i)) {
dst.SetInt(int64(i))
return nil
}
} else { // couldn't be parsed as float
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
@@ -899,6 +923,16 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
dst.SetUint(uint64(vv))
return nil
}
case string: // handle scientific notation
if i, err := strconv.ParseFloat(vv, 64); err == nil {
if 0 <= i && i <= math.MaxUint64 && !dst.OverflowUint(uint64(i)) {
dst.SetUint(uint64(i))
return nil
}
} else { // couldn't be parsed as float
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}

default:
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
@@ -1501,10 +1535,19 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
}
continue
}
k := reflect.ValueOf(d.nodeToValue(key))
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)

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")
}
} else {
k = reflect.ValueOf(d.nodeToValue(key))
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)
}
}

if k.IsValid() {
if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil {
return errors.Wrapf(err, "invalid map key")
@@ -1621,7 +1664,7 @@ func (d *Decoder) resolveReference() error {
}
}
for _, reader := range d.referenceReaders {
bytes, err := ioutil.ReadAll(reader)
bytes, err := io.ReadAll(reader)
if err != nil {
return errors.Wrapf(err, "failed to read buffer")
}
123 changes: 122 additions & 1 deletion decode_test.go
Original file line number Diff line number Diff line change
@@ -14,11 +14,12 @@ import (
"testing"
"time"

"golang.org/x/xerrors"

"github.com/goccy/go-yaml"
"github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/parser"
"golang.org/x/xerrors"
)

type Child struct {
@@ -252,6 +253,10 @@ func TestDecoder(t *testing.T) {
"v: 4294967295",
map[string]uint{"v": math.MaxUint32},
},
{
"v: 1e3",
map[string]uint{"v": 1000},
},

// uint64
{
@@ -270,6 +275,10 @@ func TestDecoder(t *testing.T) {
"v: 9223372036854775807",
map[string]uint64{"v": math.MaxInt64},
},
{
"v: 1e3",
map[string]uint64{"v": 1000},
},

// float32
{
@@ -288,6 +297,10 @@ func TestDecoder(t *testing.T) {
"v: 18446744073709551616",
map[string]float32{"v": float32(math.MaxUint64 + 1)},
},
{
"v: 1e-06",
map[string]float32{"v": 1e-6},
},

// float64
{
@@ -306,6 +319,10 @@ func TestDecoder(t *testing.T) {
"v: 18446744073709551616",
map[string]float64{"v": float64(math.MaxUint64 + 1)},
},
{
"v: 1e-06",
map[string]float64{"v": 1e-06},
},

// Timestamps
{
@@ -1092,6 +1109,73 @@ c:
}
}

func TestDecoder_ScientificNotation(t *testing.T) {
tests := []struct {
source string
value interface{}
}{
{
"v: 1e3",
map[string]uint{"v": 1000},
},
{
"v: 1e-3",
map[string]uint{"v": 0},
},
{
"v: 1e3",
map[string]int{"v": 1000},
},
{
"v: 1e-3",
map[string]int{"v": 0},
},
{
"v: 1e3",
map[string]float32{"v": 1000},
},
{
"v: 1.0e3",
map[string]float64{"v": 1000},
},
{
"v: 1e-3",
map[string]float64{"v": 0.001},
},
{
"v: 1.0e-3",
map[string]float64{"v": 0.001},
},
{
"v: 1.0e+3",
map[string]float64{"v": 1000},
},
{
"v: 1.0e+3",
map[string]float64{"v": 1000},
},
}
for _, test := range tests {
t.Run(test.source, func(t *testing.T) {
buf := bytes.NewBufferString(test.source)
dec := yaml.NewDecoder(buf)
typ := reflect.ValueOf(test.value).Type()
value := reflect.New(typ)
if err := dec.Decode(value.Interface()); err != nil {
if err == io.EOF {
return
}
t.Fatalf("%s: %+v", test.source, err)
}
actual := fmt.Sprintf("%+v", value.Elem().Interface())
expect := fmt.Sprintf("%+v", test.value)
if actual != expect {
t.Fatalf("failed to test [%s], actual=[%s], expect=[%s]", test.source, actual, expect)
}
})
}
}

func TestDecoder_TypeConversionError(t *testing.T) {
t.Run("type conversion for struct", func(t *testing.T) {
type T struct {
@@ -1114,6 +1198,17 @@ func TestDecoder_TypeConversionError(t *testing.T) {
t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
}
})
t.Run("string to uint", func(t *testing.T) {
var v T
err := yaml.Unmarshal([]byte(`b: str`), &v)
if err == nil {
t.Fatal("expected to error")
}
msg := "cannot unmarshal string into Go struct field T.B of type uint"
if !strings.Contains(err.Error(), msg) {
t.Fatalf("expected error message: %s to contain: %s", err.Error(), msg)
}
})
t.Run("string to bool", func(t *testing.T) {
var v T
err := yaml.Unmarshal([]byte(`d: str`), &v)
@@ -2906,3 +3001,29 @@ func TestSameNameInineStruct(t *testing.T) {
t.Fatalf("failed to decode")
}
}

type unmarshableMapKey struct {
Key string
}

func (mk *unmarshableMapKey) UnmarshalYAML(b []byte) error {
mk.Key = string(b)
return nil
}

func TestMapKeyCustomUnmarshaler(t *testing.T) {
var m map[unmarshableMapKey]string
if err := yaml.Unmarshal([]byte(`key: value`), &m); err != nil {
t.Fatalf("failed to unmarshal %v", err)
}
if len(m) != 1 {
t.Fatalf("expected 1 element in map, but got %d", len(m))
}
val, ok := m[unmarshableMapKey{Key: "key"}]
if !ok {
t.Fatal("expected to have element 'key' in map")
}
if val != "value" {
t.Fatalf("expected to have value \"value\", but got %q", val)
}
}
25 changes: 25 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
@@ -80,6 +80,16 @@ func TestEncoder(t *testing.T) {
map[string]float32{"v": 0.99},
nil,
},
{
"v: 1e-06\n",
map[string]float32{"v": 1e-06},
nil,
},
{
"v: 1e-06\n",
map[string]float64{"v": 0.000001},
nil,
},
{
"v: 0.123456789\n",
map[string]float64{"v": 0.123456789},
@@ -100,6 +110,16 @@ func TestEncoder(t *testing.T) {
map[string]float64{"v": 1000000},
nil,
},
{
"v: 1e-06\n",
map[string]float64{"v": 0.000001},
nil,
},
{
"v: 1e-06\n",
map[string]float64{"v": 1e-06},
nil,
},
{
"v: .inf\n",
map[string]interface{}{"v": math.Inf(0)},
@@ -318,6 +338,11 @@ func TestEncoder(t *testing.T) {
map[string]string{"a": " b "},
nil,
},
{
"a: \"`b` c\"\n",
map[string]string{"a": "`b` c"},
nil,
},
{
"a: 100.5\n",
map[string]interface{}{
Loading