Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b730b2be5d | |||
| a437caafe5 | |||
| be6c57be30 | |||
| d55304782e | |||
| 0977c05dd5 | |||
| bccd6e48f4 | |||
| 9b890cf9c5 | |||
| a3d5a0bb53 | |||
| d00d2cca6e |
@@ -15,6 +15,6 @@ jobs:
|
|||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.22"
|
go-version: "1.23"
|
||||||
- name: Run tests with coverage
|
- name: Run tests with coverage
|
||||||
run: ./ci.sh coverage -d "${GITHUB_BASE_REF-HEAD}"
|
run: ./ci.sh coverage -d "${GITHUB_BASE_REF-HEAD}"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.22"
|
go-version: "1.23"
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -30,10 +30,10 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v3
|
uses: goreleaser/goreleaser-action@v6
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
version: latest
|
version: '~> v2'
|
||||||
args: release ${{ inputs.args }} --rm-dist
|
args: release ${{ inputs.args }} --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest', 'macos-14' ]
|
os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest', 'macos-14' ]
|
||||||
go: [ '1.21', '1.22' ]
|
go: [ '1.22', '1.23' ]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
name: ${{ matrix.go }}/${{ matrix.os }}
|
name: ${{ matrix.go }}/${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
@@ -27,6 +27,6 @@ jobs:
|
|||||||
run: go test -race ./...
|
run: go test -race ./...
|
||||||
release-check:
|
release-check:
|
||||||
if: ${{ github.ref != 'refs/heads/v2' }}
|
if: ${{ github.ref != 'refs/heads/v2' }}
|
||||||
uses: pelletier/go-toml/.github/workflows/release.yml@v2
|
uses: ./.github/workflows/release.yml
|
||||||
with:
|
with:
|
||||||
args: --snapshot
|
args: --snapshot
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
version: 2
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ complete solutions exist out there.
|
|||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
Expect for parts explicitely marked otherwise, go-toml follows [Semantic
|
Expect for parts explicitly marked otherwise, go-toml follows [Semantic
|
||||||
Versioning](https://semver.org). The supported version of
|
Versioning](https://semver.org). The supported version of
|
||||||
[TOML](https://github.com/toml-lang/toml) is indicated at the beginning of this
|
[TOML](https://github.com/toml-lang/toml) is indicated at the beginning of this
|
||||||
document. The last two major versions of Go are supported (see [Go Release
|
document. The last two major versions of Go are supported (see [Go Release
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
//go:build go1.18 || go1.19 || go1.20 || go1.21 || go1.22
|
|
||||||
// +build go1.18 go1.19 go1.20 go1.21 go1.22
|
|
||||||
|
|
||||||
package toml_test
|
package toml_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
module github.com/pelletier/go-toml/v2
|
module github.com/pelletier/go-toml/v2
|
||||||
|
|
||||||
go 1.16
|
go 1.21.0
|
||||||
|
|
||||||
require github.com/stretchr/testify v1.9.0
|
require github.com/stretchr/testify v1.9.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -57,7 +57,11 @@ type SeenTracker struct {
|
|||||||
currentIdx int
|
currentIdx int
|
||||||
}
|
}
|
||||||
|
|
||||||
var pool sync.Pool
|
var pool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &SeenTracker{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SeenTracker) reset() {
|
func (s *SeenTracker) reset() {
|
||||||
// Always contains a root element at index 0.
|
// Always contains a root element at index 0.
|
||||||
@@ -331,12 +335,6 @@ func (s *SeenTracker) checkArray(node *unstable.Node) (first bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SeenTracker) checkInlineTable(node *unstable.Node) (first bool, err error) {
|
func (s *SeenTracker) checkInlineTable(node *unstable.Node) (first bool, err error) {
|
||||||
if pool.New == nil {
|
|
||||||
pool.New = func() interface{} {
|
|
||||||
return &SeenTracker{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s = pool.Get().(*SeenTracker)
|
s = pool.Get().(*SeenTracker)
|
||||||
s.reset()
|
s.reset()
|
||||||
|
|
||||||
|
|||||||
+22
-6
@@ -8,7 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -280,7 +280,7 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasTextMarshaler := v.Type().Implements(textMarshalerType)
|
hasTextMarshaler := v.Type().Implements(textMarshalerType)
|
||||||
if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
|
if hasTextMarshaler || (v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) {
|
||||||
if !hasTextMarshaler {
|
if !hasTextMarshaler {
|
||||||
v = v.Addr()
|
v = v.Addr()
|
||||||
}
|
}
|
||||||
@@ -631,6 +631,18 @@ func (enc *Encoder) keyToString(k reflect.Value) (string, error) {
|
|||||||
return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err)
|
return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err)
|
||||||
}
|
}
|
||||||
return string(keyB), nil
|
return string(keyB), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64:
|
||||||
|
return strconv.FormatInt(k.Int(), 10), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64:
|
||||||
|
return strconv.FormatUint(k.Uint(), 10), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Float32:
|
||||||
|
return strconv.FormatFloat(k.Float(), 'f', -1, 32), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Float64:
|
||||||
|
return strconv.FormatFloat(k.Float(), 'f', -1, 64), nil
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind())
|
return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind())
|
||||||
}
|
}
|
||||||
@@ -668,8 +680,8 @@ func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sortEntriesByKey(e []entry) {
|
func sortEntriesByKey(e []entry) {
|
||||||
sort.Slice(e, func(i, j int) bool {
|
slices.SortFunc(e, func(a, b entry) int {
|
||||||
return e[i].Key < e[j].Key
|
return strings.Compare(a.Key, b.Key)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,7 +744,7 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
|
|||||||
if fieldType.Anonymous {
|
if fieldType.Anonymous {
|
||||||
if fieldType.Type.Kind() == reflect.Struct {
|
if fieldType.Type.Kind() == reflect.Struct {
|
||||||
walkStruct(ctx, t, f)
|
walkStruct(ctx, t, f)
|
||||||
} else if fieldType.Type.Kind() == reflect.Pointer && !f.IsNil() && f.Elem().Kind() == reflect.Struct {
|
} else if fieldType.Type.Kind() == reflect.Ptr && !f.IsNil() && f.Elem().Kind() == reflect.Struct {
|
||||||
walkStruct(ctx, t, f.Elem())
|
walkStruct(ctx, t, f.Elem())
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -951,7 +963,7 @@ func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
|
|||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) {
|
if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1025,6 +1037,10 @@ func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.
|
|||||||
|
|
||||||
scratch = enc.commented(ctx.commented, scratch)
|
scratch = enc.commented(ctx.commented, scratch)
|
||||||
|
|
||||||
|
if enc.indentTables {
|
||||||
|
scratch = enc.indent(ctx.indent, scratch)
|
||||||
|
}
|
||||||
|
|
||||||
scratch = append(scratch, "[["...)
|
scratch = append(scratch, "[["...)
|
||||||
|
|
||||||
for i, k := range ctx.parentKey {
|
for i, k := range ctx.parentKey {
|
||||||
|
|||||||
+95
-3
@@ -587,13 +587,69 @@ foo = 42
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "invalid map key",
|
desc: "int map key",
|
||||||
v: map[int]interface{}{1: "a"},
|
v: map[int]interface{}{1: "a"},
|
||||||
|
expected: `1 = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "int8 map key",
|
||||||
|
v: map[int8]interface{}{1: "a"},
|
||||||
|
expected: `1 = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "int64 map key",
|
||||||
|
v: map[int64]interface{}{1: "a"},
|
||||||
|
expected: `1 = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "uint map key",
|
||||||
|
v: map[uint]interface{}{1: "a"},
|
||||||
|
expected: `1 = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "uint8 map key",
|
||||||
|
v: map[uint8]interface{}{1: "a"},
|
||||||
|
expected: `1 = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "uint64 map key",
|
||||||
|
v: map[uint64]interface{}{1: "a"},
|
||||||
|
expected: `1 = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float32 map key",
|
||||||
|
v: map[float32]interface{}{
|
||||||
|
1.1: "a",
|
||||||
|
1.0020: "b",
|
||||||
|
},
|
||||||
|
expected: `'1.002' = 'b'
|
||||||
|
'1.1' = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "float64 map key",
|
||||||
|
v: map[float64]interface{}{
|
||||||
|
1.1: "a",
|
||||||
|
1.0020: "b",
|
||||||
|
},
|
||||||
|
expected: `'1.002' = 'b'
|
||||||
|
'1.1' = 'a'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid map key",
|
||||||
|
v: map[struct{ int }]interface{}{{1}: "a"},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "invalid map key but empty",
|
desc: "invalid map key but empty",
|
||||||
v: map[int]interface{}{},
|
v: map[struct{ int }]interface{}{},
|
||||||
expected: "",
|
expected: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1499,6 +1555,43 @@ func TestMarshalCommented(t *testing.T) {
|
|||||||
require.Equal(t, expected, string(out))
|
require.Equal(t, expected, string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshalIndentedCustomTypeArray(t *testing.T) {
|
||||||
|
c := struct {
|
||||||
|
Nested struct {
|
||||||
|
NestedArray []struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
Nested: struct {
|
||||||
|
NestedArray []struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
NestedArray: []struct {
|
||||||
|
Value int
|
||||||
|
}{
|
||||||
|
{Value: 1},
|
||||||
|
{Value: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `[Nested]
|
||||||
|
[[Nested.NestedArray]]
|
||||||
|
Value = 1
|
||||||
|
|
||||||
|
[[Nested.NestedArray]]
|
||||||
|
Value = 2
|
||||||
|
`
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := toml.NewEncoder(&buf)
|
||||||
|
enc.SetIndentTables(true)
|
||||||
|
require.NoError(t, enc.Encode(c))
|
||||||
|
require.Equal(t, expected, buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleMarshal() {
|
func ExampleMarshal() {
|
||||||
type MyConfig struct {
|
type MyConfig struct {
|
||||||
Version int
|
Version int
|
||||||
@@ -1528,7 +1621,6 @@ func ExampleMarshal() {
|
|||||||
// configuration file that has commented out sections (example from
|
// configuration file that has commented out sections (example from
|
||||||
// go-graphite/graphite-clickhouse).
|
// go-graphite/graphite-clickhouse).
|
||||||
func ExampleMarshal_commented() {
|
func ExampleMarshal_commented() {
|
||||||
|
|
||||||
type Common struct {
|
type Common struct {
|
||||||
Listen string `toml:"listen" comment:"general listener"`
|
Listen string `toml:"listen" comment:"general listener"`
|
||||||
PprofListen string `toml:"pprof-listen" comment:"listener to serve /debug/pprof requests. '-pprof' argument overrides it"`
|
PprofListen string `toml:"pprof-listen" comment:"listener to serve /debug/pprof requests. '-pprof' argument overrides it"`
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
//go:build go1.18 || go1.19 || go1.20 || go1.21 || go1.22
|
|
||||||
// +build go1.18 go1.19 go1.20 go1.21 go1.22
|
|
||||||
|
|
||||||
package ossfuzz
|
package ossfuzz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
+34
-11
@@ -5,9 +5,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -21,10 +21,8 @@ import (
|
|||||||
//
|
//
|
||||||
// It is a shortcut for Decoder.Decode() with the default options.
|
// It is a shortcut for Decoder.Decode() with the default options.
|
||||||
func Unmarshal(data []byte, v interface{}) error {
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
p := unstable.Parser{}
|
d := decoder{}
|
||||||
p.Reset(data)
|
d.p.Reset(data)
|
||||||
d := decoder{p: &p}
|
|
||||||
|
|
||||||
return d.FromParser(v)
|
return d.FromParser(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,27 +115,25 @@ func (d *Decoder) EnableUnmarshalerInterface() *Decoder {
|
|||||||
// Inline Table -> same as Table
|
// Inline Table -> same as Table
|
||||||
// Array of Tables -> same as Array and Table
|
// Array of Tables -> same as Array and Table
|
||||||
func (d *Decoder) Decode(v interface{}) error {
|
func (d *Decoder) Decode(v interface{}) error {
|
||||||
b, err := ioutil.ReadAll(d.r)
|
b, err := io.ReadAll(d.r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("toml: %w", err)
|
return fmt.Errorf("toml: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p := unstable.Parser{}
|
|
||||||
p.Reset(b)
|
|
||||||
dec := decoder{
|
dec := decoder{
|
||||||
p: &p,
|
|
||||||
strict: strict{
|
strict: strict{
|
||||||
Enabled: d.strict,
|
Enabled: d.strict,
|
||||||
},
|
},
|
||||||
unmarshalerInterface: d.unmarshalerInterface,
|
unmarshalerInterface: d.unmarshalerInterface,
|
||||||
}
|
}
|
||||||
|
dec.p.Reset(b)
|
||||||
|
|
||||||
return dec.FromParser(v)
|
return dec.FromParser(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type decoder struct {
|
type decoder struct {
|
||||||
// Which parser instance in use for this decoding session.
|
// Which parser instance in use for this decoding session.
|
||||||
p *unstable.Parser
|
p unstable.Parser
|
||||||
|
|
||||||
// Flag indicating that the current expression is stashed.
|
// Flag indicating that the current expression is stashed.
|
||||||
// If set to true, calling nextExpr will not actually pull a new expression
|
// If set to true, calling nextExpr will not actually pull a new expression
|
||||||
@@ -1078,12 +1074,39 @@ func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value,
|
|||||||
}
|
}
|
||||||
return mk, nil
|
return mk, nil
|
||||||
|
|
||||||
case reflect.PtrTo(keyType).Implements(textUnmarshalerType):
|
case reflect.PointerTo(keyType).Implements(textUnmarshalerType):
|
||||||
mk := reflect.New(keyType)
|
mk := reflect.New(keyType)
|
||||||
if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
|
if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil {
|
||||||
return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err)
|
return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err)
|
||||||
}
|
}
|
||||||
return mk.Elem(), nil
|
return mk.Elem(), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64:
|
||||||
|
key, err := strconv.ParseInt(string(data), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from integer: %w", stringType, err)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(key).Convert(keyType), nil
|
||||||
|
case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64:
|
||||||
|
key, err := strconv.ParseUint(string(data), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from unsigned integer: %w", stringType, err)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(key).Convert(keyType), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Float32:
|
||||||
|
key, err := strconv.ParseFloat(string(data), 32)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(float32(key)), nil
|
||||||
|
|
||||||
|
case keyType.Kind() == reflect.Float64:
|
||||||
|
key, err := strconv.ParseFloat(string(data), 64)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(float64(key)), nil
|
||||||
}
|
}
|
||||||
return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType)
|
return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType)
|
||||||
}
|
}
|
||||||
|
|||||||
+153
-19
@@ -205,7 +205,6 @@ func TestUnmarshal_Floats(t *testing.T) {
|
|||||||
testFn func(t *testing.T, v float64)
|
testFn func(t *testing.T, v float64)
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
|
|
||||||
{
|
{
|
||||||
desc: "float pi",
|
desc: "float pi",
|
||||||
input: `3.1415`,
|
input: `3.1415`,
|
||||||
@@ -840,8 +839,10 @@ huey = 'dewey'
|
|||||||
|
|
||||||
return test{
|
return test{
|
||||||
target: &doc{},
|
target: &doc{},
|
||||||
expected: &doc{A: []interface{}{"0", "1", "2", "3", "4", "5", "6",
|
expected: &doc{A: []interface{}{
|
||||||
"7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}},
|
"0", "1", "2", "3", "4", "5", "6",
|
||||||
|
"7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17",
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1696,16 +1697,6 @@ B = "data"`,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
desc: "empty map into map with invalid key type",
|
|
||||||
input: ``,
|
|
||||||
gen: func() test {
|
|
||||||
return test{
|
|
||||||
target: &map[int]string{},
|
|
||||||
expected: &map[int]string{},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "into map with convertible key type",
|
desc: "into map with convertible key type",
|
||||||
input: `A = "hello"`,
|
input: `A = "hello"`,
|
||||||
@@ -1942,6 +1933,150 @@ B = "data"`,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of int to string",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[int]string{},
|
||||||
|
expected: &map[int]string{1: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of int8 to string",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[int8]string{},
|
||||||
|
expected: &map[int8]string{1: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of int64 to string",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[int64]string{},
|
||||||
|
expected: &map[int64]string{1: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of uint to string",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[uint]string{},
|
||||||
|
expected: &map[uint]string{1: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of uint8 to string",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[uint8]string{},
|
||||||
|
expected: &map[uint8]string{1: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of uint64 to string",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[uint64]string{},
|
||||||
|
expected: &map[uint64]string{1: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of uint with invalid key",
|
||||||
|
input: `-1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[uint]string{},
|
||||||
|
err: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of float64 to string",
|
||||||
|
input: `'1.01' = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[float64]string{},
|
||||||
|
expected: &map[float64]string{1.01: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of float64 with invalid key",
|
||||||
|
input: `key = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[float64]string{},
|
||||||
|
err: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of float32 to string",
|
||||||
|
input: `'1.01' = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[float32]string{},
|
||||||
|
expected: &map[float32]string{1.01: "a"},
|
||||||
|
assert: func(t *testing.T, test test) {
|
||||||
|
assert.Equal(t, test.expected, test.target)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "into map of float32 with invalid key",
|
||||||
|
input: `key = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[float32]string{},
|
||||||
|
err: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid map key type",
|
||||||
|
input: `1 = "a"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[struct{ int }]string{},
|
||||||
|
err: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
@@ -2653,7 +2788,7 @@ func TestIssue772(t *testing.T) {
|
|||||||
FileHandling `toml:"filehandling"`
|
FileHandling `toml:"filehandling"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultConfigFile = []byte(`
|
defaultConfigFile := []byte(`
|
||||||
[filehandling]
|
[filehandling]
|
||||||
pattern = "reach-masterdev-"`)
|
pattern = "reach-masterdev-"`)
|
||||||
|
|
||||||
@@ -2750,7 +2885,7 @@ func TestIssue866(t *testing.T) {
|
|||||||
PipelineMapping map[string]*Pipeline `toml:"pipelines"`
|
PipelineMapping map[string]*Pipeline `toml:"pipelines"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var badToml = `
|
badToml := `
|
||||||
[pipelines.register]
|
[pipelines.register]
|
||||||
mapping.inst.req = [
|
mapping.inst.req = [
|
||||||
["param1", "value1"],
|
["param1", "value1"],
|
||||||
@@ -2768,7 +2903,7 @@ mapping.inst.res = [
|
|||||||
t.Fatal("unmarshal failed with mismatch value")
|
t.Fatal("unmarshal failed with mismatch value")
|
||||||
}
|
}
|
||||||
|
|
||||||
var goodTooToml = `
|
goodTooToml := `
|
||||||
[pipelines.register]
|
[pipelines.register]
|
||||||
mapping.inst.req = [
|
mapping.inst.req = [
|
||||||
["param1", "value1"],
|
["param1", "value1"],
|
||||||
@@ -2783,7 +2918,7 @@ mapping.inst.req = [
|
|||||||
t.Fatal("unmarshal failed with mismatch value")
|
t.Fatal("unmarshal failed with mismatch value")
|
||||||
}
|
}
|
||||||
|
|
||||||
var goodToml = `
|
goodToml := `
|
||||||
[pipelines.register.mapping.inst]
|
[pipelines.register.mapping.inst]
|
||||||
req = [
|
req = [
|
||||||
["param1", "value1"],
|
["param1", "value1"],
|
||||||
@@ -3362,7 +3497,7 @@ func TestOmitEmpty(t *testing.T) {
|
|||||||
X []elem `toml:",inline"`
|
X []elem `toml:",inline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
d := doc{X: []elem{elem{
|
d := doc{X: []elem{{
|
||||||
Foo: "test",
|
Foo: "test",
|
||||||
Inner: inner{
|
Inner: inner{
|
||||||
V: "alue",
|
V: "alue",
|
||||||
@@ -3785,7 +3920,6 @@ func (k *CustomUnmarshalerKey) UnmarshalTOML(value *unstable.Node) error {
|
|||||||
}
|
}
|
||||||
k.A = item
|
k.A = item
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshal_CustomUnmarshaler(t *testing.T) {
|
func TestUnmarshal_CustomUnmarshaler(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user