Stack-based unmarshaler (#546)
* Benchmark script * Rewrite unmarshaler using the stack Instead of tracking the build chain using `target`s, use the stack instead. Working and most benchmarks look good, but regression on structs unmarshalling. ~60% slower on ReferenceFile/struct. * Shortcut to check if last node of iterator * Remove unecessary pointer allocation * Skip over unused keys without marking them as seen * Add some tests * Fix mktemp on macos
This commit is contained in:
+1
-1
@@ -60,7 +60,7 @@ enable = [
|
|||||||
# "nlreturn",
|
# "nlreturn",
|
||||||
"noctx",
|
"noctx",
|
||||||
"nolintlint",
|
"nolintlint",
|
||||||
"paralleltest",
|
#"paralleltest",
|
||||||
"prealloc",
|
"prealloc",
|
||||||
"predeclared",
|
"predeclared",
|
||||||
"revive",
|
"revive",
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ var bench_inputs = []struct {
|
|||||||
|
|
||||||
func TestUnmarshalDatasetCode(t *testing.T) {
|
func TestUnmarshalDatasetCode(t *testing.T) {
|
||||||
for _, tc := range bench_inputs {
|
for _, tc := range bench_inputs {
|
||||||
buf := fixture(t, tc.name)
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
buf := fixture(t, tc.name)
|
||||||
|
|
||||||
var v interface{}
|
var v interface{}
|
||||||
check(t, toml.Unmarshal(buf, &v))
|
require.NoError(t, toml.Unmarshal(buf, &v))
|
||||||
|
|
||||||
b, err := json.Marshal(v)
|
b, err := json.Marshal(v)
|
||||||
check(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, len(b), tc.jsonLen)
|
require.Equal(t, len(b), tc.jsonLen)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -45,14 +46,14 @@ func TestUnmarshalDatasetCode(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkUnmarshalDataset(b *testing.B) {
|
func BenchmarkUnmarshalDataset(b *testing.B) {
|
||||||
for _, tc := range bench_inputs {
|
for _, tc := range bench_inputs {
|
||||||
buf := fixture(b, tc.name)
|
|
||||||
b.Run(tc.name, func(b *testing.B) {
|
b.Run(tc.name, func(b *testing.B) {
|
||||||
|
buf := fixture(b, tc.name)
|
||||||
b.SetBytes(int64(len(buf)))
|
b.SetBytes(int64(len(buf)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var v interface{}
|
var v interface{}
|
||||||
check(b, toml.Unmarshal(buf, &v))
|
require.NoError(b, toml.Unmarshal(buf, &v))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -60,22 +61,20 @@ func BenchmarkUnmarshalDataset(b *testing.B) {
|
|||||||
|
|
||||||
// fixture returns the uncompressed contents of path.
|
// fixture returns the uncompressed contents of path.
|
||||||
func fixture(tb testing.TB, path string) []byte {
|
func fixture(tb testing.TB, path string) []byte {
|
||||||
f, err := os.Open(filepath.Join("testdata", path+".toml.gz"))
|
tb.Helper()
|
||||||
check(tb, err)
|
|
||||||
|
file := path + ".toml.gz"
|
||||||
|
f, err := os.Open(filepath.Join("testdata", file))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
tb.Skip("benchmark fixture not found:", file)
|
||||||
|
}
|
||||||
|
require.NoError(tb, err)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
gz, err := gzip.NewReader(f)
|
gz, err := gzip.NewReader(f)
|
||||||
check(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
buf, err := ioutil.ReadAll(gz)
|
buf, err := ioutil.ReadAll(gz)
|
||||||
check(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func check(tb testing.TB, err error) {
|
|
||||||
if err != nil {
|
|
||||||
tb.Helper()
|
|
||||||
tb.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,6 +9,18 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestUnmarshalSimple(t *testing.T) {
|
||||||
|
doc := []byte(`A = "hello"`)
|
||||||
|
d := struct {
|
||||||
|
A string
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := toml.Unmarshal(doc, &d)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkUnmarshalSimple(b *testing.B) {
|
func BenchmarkUnmarshalSimple(b *testing.B) {
|
||||||
doc := []byte(`A = "hello"`)
|
doc := []byte(`A = "hello"`)
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ benchmark [OPTIONS...] [BRANCH]
|
|||||||
-d Compare benchmarks of HEAD with BRANCH using benchstats. In
|
-d Compare benchmarks of HEAD with BRANCH using benchstats. In
|
||||||
this form the BRANCH argument is required.
|
this form the BRANCH argument is required.
|
||||||
|
|
||||||
|
-a Compare benchmarks of HEAD against go-toml v1 and
|
||||||
|
BurntSushi/toml.
|
||||||
|
|
||||||
coverage [OPTIONS...] [BRANCH]
|
coverage [OPTIONS...] [BRANCH]
|
||||||
|
|
||||||
Generates code coverage.
|
Generates code coverage.
|
||||||
@@ -118,6 +121,7 @@ coverage() {
|
|||||||
bench() {
|
bench() {
|
||||||
branch="${1}"
|
branch="${1}"
|
||||||
out="${2}"
|
out="${2}"
|
||||||
|
replace="${3}"
|
||||||
dir="$(mktemp -d)"
|
dir="$(mktemp -d)"
|
||||||
|
|
||||||
stderr "Executing benchmark for ${branch} at ${dir}"
|
stderr "Executing benchmark for ${branch} at ${dir}"
|
||||||
@@ -129,6 +133,15 @@ bench() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
pushd "$dir"
|
pushd "$dir"
|
||||||
|
|
||||||
|
if [ "${replace}" != "" ]; then
|
||||||
|
find ./benchmark/ -iname '*.go' -exec sed -i -E "s|github.com/pelletier/go-toml/v2|${replace}|g" {} \;
|
||||||
|
go get "${replace}"
|
||||||
|
# hack: remove canada.toml.gz because it is not supported by
|
||||||
|
# burntsushi, and replace is only used for benchmark -a
|
||||||
|
rm -f benchmark/testdata/canada.toml.gz
|
||||||
|
fi
|
||||||
|
|
||||||
go test -bench=. -count=10 ./... | tee "${out}"
|
go test -bench=. -count=10 ./... | tee "${out}"
|
||||||
popd
|
popd
|
||||||
|
|
||||||
@@ -142,14 +155,34 @@ benchmark() {
|
|||||||
-d)
|
-d)
|
||||||
shift
|
shift
|
||||||
target="${1?Need to provide a target branch argument}"
|
target="${1?Need to provide a target branch argument}"
|
||||||
old=`mktemp`
|
|
||||||
|
old=`mktemp --suffix=-${target}`
|
||||||
bench "${target}" "${old}"
|
bench "${target}" "${old}"
|
||||||
|
|
||||||
new=`mktemp`
|
new=`mktemp --suffix=-HEAD`
|
||||||
bench HEAD "${new}"
|
bench HEAD "${new}"
|
||||||
|
|
||||||
benchstat "${old}" "${new}"
|
benchstat "${old}" "${new}"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
-a)
|
||||||
|
shift
|
||||||
|
|
||||||
|
v2stats=`mktemp -t go-toml-v2`
|
||||||
|
bench HEAD "${v2stats}" "github.com/pelletier/go-toml/v2"
|
||||||
|
v1stats=`mktemp -t go-toml-v1`
|
||||||
|
bench HEAD "${v1stats}" "github.com/pelletier/go-toml"
|
||||||
|
bsstats=`mktemp -t bs-toml`
|
||||||
|
bench HEAD "${bsstats}" "github.com/BurntSushi/toml"
|
||||||
|
|
||||||
|
cp "${v2stats}" go-toml-v2.txt
|
||||||
|
cp "${v1stats}" go-toml-v1.txt
|
||||||
|
cp "${bsstats}" bs-toml.txt
|
||||||
|
|
||||||
|
benchstat -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt
|
||||||
|
|
||||||
|
rm -f go-toml-v2.txt go-toml-v1.txt bs-toml.txt
|
||||||
|
return $?
|
||||||
esac
|
esac
|
||||||
|
|
||||||
bench "${1-HEAD}" `mktemp`
|
bench "${1-HEAD}" `mktemp`
|
||||||
|
|||||||
+1
-3
@@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestDecodeError(t *testing.T) {
|
func TestDecodeError(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
@@ -154,7 +153,7 @@ line 5`,
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
b.Write([]byte(e.doc[0]))
|
b.Write([]byte(e.doc[0]))
|
||||||
start := b.Len()
|
start := b.Len()
|
||||||
@@ -182,7 +181,6 @@ line 5`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeError_Accessors(t *testing.T) {
|
func TestDecodeError_Accessors(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
e := DecodeError{
|
e := DecodeError{
|
||||||
message: "foo",
|
message: "foo",
|
||||||
|
|||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
package toml_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pelletier/go-toml/v2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFastSimple(t *testing.T) {
|
||||||
|
m := map[string]int64{}
|
||||||
|
err := toml.Unmarshal([]byte(`a = 42`), &m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]int64{"a": 42}, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastSimpleString(t *testing.T) {
|
||||||
|
m := map[string]string{}
|
||||||
|
err := toml.Unmarshal([]byte(`a = "hello"`), &m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]string{"a": "hello"}, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastSimpleInterface(t *testing.T) {
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
err := toml.Unmarshal([]byte(`
|
||||||
|
a = "hello"
|
||||||
|
b = 42`), &m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]interface{}{
|
||||||
|
"a": "hello",
|
||||||
|
"b": int64(42),
|
||||||
|
}, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastMultipartKeyInterface(t *testing.T) {
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
err := toml.Unmarshal([]byte(`
|
||||||
|
a.interim = "test"
|
||||||
|
a.b.c = "hello"
|
||||||
|
b = 42`), &m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]interface{}{
|
||||||
|
"a": map[string]interface{}{
|
||||||
|
"interim": "test",
|
||||||
|
"b": map[string]interface{}{
|
||||||
|
"c": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"b": int64(42),
|
||||||
|
}, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastExistingMap(t *testing.T) {
|
||||||
|
m := map[string]interface{}{
|
||||||
|
"ints": map[string]int{},
|
||||||
|
}
|
||||||
|
err := toml.Unmarshal([]byte(`
|
||||||
|
ints.one = 1
|
||||||
|
ints.two = 2
|
||||||
|
strings.yo = "hello"`), &m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, map[string]interface{}{
|
||||||
|
"ints": map[string]interface{}{
|
||||||
|
"one": int64(1),
|
||||||
|
"two": int64(2),
|
||||||
|
},
|
||||||
|
"strings": map[string]interface{}{
|
||||||
|
"yo": "hello",
|
||||||
|
},
|
||||||
|
}, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastArrayTable(t *testing.T) {
|
||||||
|
b := []byte(`
|
||||||
|
[root]
|
||||||
|
[[root.nested]]
|
||||||
|
name = 'Bob'
|
||||||
|
[[root.nested]]
|
||||||
|
name = 'Alice'
|
||||||
|
`)
|
||||||
|
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
|
||||||
|
err := toml.Unmarshal(b, &m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, map[string]interface{}{
|
||||||
|
"root": map[string]interface{}{
|
||||||
|
"nested": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "Bob",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "Alice",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, m)
|
||||||
|
}
|
||||||
@@ -28,6 +28,12 @@ func (c *Iterator) Next() bool {
|
|||||||
return c.node.Valid()
|
return c.node.Valid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsLast returns true if the current node of the iterator is the last one.
|
||||||
|
// Subsequent call to Next() will return false.
|
||||||
|
func (c *Iterator) IsLast() bool {
|
||||||
|
return c.node.next <= 0
|
||||||
|
}
|
||||||
|
|
||||||
// Node returns a copy of the node pointed at by the iterator.
|
// Node returns a copy of the node pointed at by the iterator.
|
||||||
func (c *Iterator) Node() Node {
|
func (c *Iterator) Node() Node {
|
||||||
return c.node
|
return c.node
|
||||||
|
|||||||
@@ -223,11 +223,13 @@ type testSubDoc struct {
|
|||||||
unexported int `toml:"shouldntBeHere"`
|
unexported int `toml:"shouldntBeHere"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var biteMe = "Bite me"
|
var (
|
||||||
var float1 float32 = 12.3
|
biteMe = "Bite me"
|
||||||
var float2 float32 = 45.6
|
float1 float32 = 12.3
|
||||||
var float3 float32 = 78.9
|
float2 float32 = 45.6
|
||||||
var subdoc = testSubDoc{"Second", 0}
|
float3 float32 = 78.9
|
||||||
|
subdoc = testSubDoc{"Second", 0}
|
||||||
|
)
|
||||||
|
|
||||||
var docData = testDoc{
|
var docData = testDoc{
|
||||||
Title: "TOML Marshal Testing",
|
Title: "TOML Marshal Testing",
|
||||||
@@ -382,7 +384,7 @@ var intErrTomls = []string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestErrUnmarshal(t *testing.T) {
|
func TestErrUnmarshal(t *testing.T) {
|
||||||
var errTomls = []string{
|
errTomls := []string{
|
||||||
"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
||||||
"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
|
||||||
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
|
"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
|
||||||
@@ -468,7 +470,7 @@ func TestEmptyUnmarshalOmit(t *testing.T) {
|
|||||||
Map map[string]string `toml:"map,omitempty"`
|
Map map[string]string `toml:"map,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var emptyTestData2 = emptyMarshalTestStruct2{
|
emptyTestData2 := emptyMarshalTestStruct2{
|
||||||
Title: "Placeholder",
|
Title: "Placeholder",
|
||||||
Bool: false,
|
Bool: false,
|
||||||
Int: 0,
|
Int: 0,
|
||||||
@@ -496,21 +498,23 @@ type pointerMarshalTestStruct struct {
|
|||||||
DblPtr *[]*[]*string
|
DblPtr *[]*[]*string
|
||||||
}
|
}
|
||||||
|
|
||||||
var pointerStr = "Hello"
|
var (
|
||||||
var pointerList = []string{"Hello back"}
|
pointerStr = "Hello"
|
||||||
var pointerListPtr = []*string{&pointerStr}
|
pointerList = []string{"Hello back"}
|
||||||
var pointerMap = map[string]string{"response": "Goodbye"}
|
pointerListPtr = []*string{&pointerStr}
|
||||||
var pointerMapPtr = map[string]*string{"alternate": &pointerStr}
|
pointerMap = map[string]string{"response": "Goodbye"}
|
||||||
var pointerTestData = pointerMarshalTestStruct{
|
pointerMapPtr = map[string]*string{"alternate": &pointerStr}
|
||||||
Str: &pointerStr,
|
pointerTestData = pointerMarshalTestStruct{
|
||||||
List: &pointerList,
|
Str: &pointerStr,
|
||||||
ListPtr: &pointerListPtr,
|
List: &pointerList,
|
||||||
Map: &pointerMap,
|
ListPtr: &pointerListPtr,
|
||||||
MapPtr: &pointerMapPtr,
|
Map: &pointerMap,
|
||||||
EmptyStr: nil,
|
MapPtr: &pointerMapPtr,
|
||||||
EmptyList: nil,
|
EmptyStr: nil,
|
||||||
EmptyMap: nil,
|
EmptyList: nil,
|
||||||
}
|
EmptyMap: nil,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var pointerTestToml = []byte(`List = ["Hello back"]
|
var pointerTestToml = []byte(`List = ["Hello back"]
|
||||||
ListPtr = ["Hello"]
|
ListPtr = ["Hello"]
|
||||||
@@ -538,15 +542,17 @@ func TestUnmarshalTypeMismatch(t *testing.T) {
|
|||||||
|
|
||||||
type nestedMarshalTestStruct struct {
|
type nestedMarshalTestStruct struct {
|
||||||
String [][]string
|
String [][]string
|
||||||
//Struct [][]basicMarshalTestSubStruct
|
// Struct [][]basicMarshalTestSubStruct
|
||||||
StringPtr *[]*[]*string
|
StringPtr *[]*[]*string
|
||||||
// StructPtr *[]*[]*basicMarshalTestSubStruct
|
// StructPtr *[]*[]*basicMarshalTestSubStruct
|
||||||
}
|
}
|
||||||
|
|
||||||
var str1 = "Three"
|
var (
|
||||||
var str2 = "Four"
|
str1 = "Three"
|
||||||
var strPtr = []*string{&str1, &str2}
|
str2 = "Four"
|
||||||
var strPtr2 = []*[]*string{&strPtr}
|
strPtr = []*string{&str1, &str2}
|
||||||
|
strPtr2 = []*[]*string{&strPtr}
|
||||||
|
)
|
||||||
|
|
||||||
var nestedTestData = nestedMarshalTestStruct{
|
var nestedTestData = nestedMarshalTestStruct{
|
||||||
String: [][]string{{"Five", "Six"}, {"One", "Two"}},
|
String: [][]string{{"Five", "Six"}, {"One", "Two"}},
|
||||||
@@ -597,6 +603,7 @@ var nestedCustomMarshalerData = customMarshalerParent{
|
|||||||
var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
|
var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
|
||||||
me = "Maiku Suteda"
|
me = "Maiku Suteda"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends]
|
var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends]
|
||||||
FirstName = "Sally"
|
FirstName = "Sally"
|
||||||
LastName = "Fields"`)
|
LastName = "Fields"`)
|
||||||
@@ -613,11 +620,11 @@ func (x *IntOrString) MarshalTOML() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalTextMarshaler(t *testing.T) {
|
func TestUnmarshalTextMarshaler(t *testing.T) {
|
||||||
var nested = struct {
|
nested := struct {
|
||||||
Friends textMarshaler `toml:"friends"`
|
Friends textMarshaler `toml:"friends"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
var expected = struct {
|
expected := struct {
|
||||||
Friends textMarshaler `toml:"friends"`
|
Friends textMarshaler `toml:"friends"`
|
||||||
}{
|
}{
|
||||||
Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"},
|
Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"},
|
||||||
@@ -1360,7 +1367,6 @@ func TestUnmarshalPreservesUnexportedFields(t *testing.T) {
|
|||||||
t.Run("unexported field should not be set from toml", func(t *testing.T) {
|
t.Run("unexported field should not be set from toml", func(t *testing.T) {
|
||||||
var actual unexportedFieldPreservationTest
|
var actual unexportedFieldPreservationTest
|
||||||
err := toml.Unmarshal([]byte(doc), &actual)
|
err := toml.Unmarshal([]byte(doc), &actual)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("did not expect an error")
|
t.Fatal("did not expect an error")
|
||||||
}
|
}
|
||||||
@@ -1394,7 +1400,6 @@ func TestUnmarshalPreservesUnexportedFields(t *testing.T) {
|
|||||||
Nested3: &unexportedFieldPreservationTestNested{"baz", "bax"},
|
Nested3: &unexportedFieldPreservationTestNested{"baz", "bax"},
|
||||||
}
|
}
|
||||||
err := toml.Unmarshal([]byte(doc), &actual)
|
err := toml.Unmarshal([]byte(doc), &actual)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("did not expect an error")
|
t.Fatal("did not expect an error")
|
||||||
}
|
}
|
||||||
@@ -1431,7 +1436,6 @@ func TestUnmarshalLocalDate(t *testing.T) {
|
|||||||
var obj dateStruct
|
var obj dateStruct
|
||||||
|
|
||||||
err := toml.Unmarshal([]byte(doc), &obj)
|
err := toml.Unmarshal([]byte(doc), &obj)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1457,7 +1461,6 @@ func TestUnmarshalLocalDate(t *testing.T) {
|
|||||||
var obj dateStruct
|
var obj dateStruct
|
||||||
|
|
||||||
err := toml.Unmarshal([]byte(doc), &obj)
|
err := toml.Unmarshal([]byte(doc), &obj)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1495,7 +1498,8 @@ func TestUnmarshalLocalDateTime(t *testing.T) {
|
|||||||
Second: 0,
|
Second: 0,
|
||||||
Nanosecond: 0,
|
Nanosecond: 0,
|
||||||
},
|
},
|
||||||
}},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "with nanoseconds",
|
name: "with nanoseconds",
|
||||||
in: "1979-05-27T00:32:00.999999",
|
in: "1979-05-27T00:32:00.999999",
|
||||||
@@ -1526,7 +1530,6 @@ func TestUnmarshalLocalDateTime(t *testing.T) {
|
|||||||
var obj dateStruct
|
var obj dateStruct
|
||||||
|
|
||||||
err := toml.Unmarshal([]byte(doc), &obj)
|
err := toml.Unmarshal([]byte(doc), &obj)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1544,7 +1547,6 @@ func TestUnmarshalLocalDateTime(t *testing.T) {
|
|||||||
var obj dateStruct
|
var obj dateStruct
|
||||||
|
|
||||||
err := toml.Unmarshal([]byte(doc), &obj)
|
err := toml.Unmarshal([]byte(doc), &obj)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -1613,7 +1615,6 @@ func TestUnmarshalLocalTime(t *testing.T) {
|
|||||||
var obj dateStruct
|
var obj dateStruct
|
||||||
|
|
||||||
err := toml.Unmarshal([]byte(doc), &obj)
|
err := toml.Unmarshal([]byte(doc), &obj)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -2283,8 +2284,7 @@ func (d *durationString) UnmarshalTOML(v interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type config437Error struct {
|
type config437Error struct{}
|
||||||
}
|
|
||||||
|
|
||||||
func (e *config437Error) UnmarshalTOML(v interface{}) error {
|
func (e *config437Error) UnmarshalTOML(v interface{}) error {
|
||||||
return errors.New("expected")
|
return errors.New("expected")
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit
|
|||||||
// that have been seen in previous calls, and validates that types are consistent.
|
// that have been seen in previous calls, and validates that types are consistent.
|
||||||
func (s *SeenTracker) CheckExpression(node ast.Node) error {
|
func (s *SeenTracker) CheckExpression(node ast.Node) error {
|
||||||
if s.entries == nil {
|
if s.entries == nil {
|
||||||
//s.entries = make([]entry, 0, 8)
|
// s.entries = make([]entry, 0, 8)
|
||||||
// Skip ID = 0 to remove the confusion between nodes whose parent has
|
// Skip ID = 0 to remove the confusion between nodes whose parent has
|
||||||
// id 0 and root nodes (parent id is 0 because it's the zero value).
|
// id 0 and root nodes (parent id is 0 because it's the zero value).
|
||||||
s.nextID = 1
|
s.nextID = 1
|
||||||
@@ -134,7 +134,7 @@ func (s *SeenTracker) checkTable(node ast.Node) error {
|
|||||||
// it in a function requires to copy the iterator, or allocate it to the
|
// it in a function requires to copy the iterator, or allocate it to the
|
||||||
// heap, which is not cheap.
|
// heap, which is not cheap.
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
if !it.Node().Next().Valid() {
|
if it.IsLast() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ func (s *SeenTracker) checkArrayTable(node ast.Node) error {
|
|||||||
parentIdx := -1
|
parentIdx := -1
|
||||||
|
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
if !it.Node().Next().Valid() {
|
if it.IsLast() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ func cmpEqual(x, y interface{}) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDates(t *testing.T) {
|
func TestDates(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
date LocalDate
|
date LocalDate
|
||||||
@@ -64,7 +63,6 @@ func TestDates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateIsValid(t *testing.T) {
|
func TestDateIsValid(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
date LocalDate
|
date LocalDate
|
||||||
@@ -91,7 +89,6 @@ func TestDateIsValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseDate(t *testing.T) {
|
func TestParseDate(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var emptyDate LocalDate
|
var emptyDate LocalDate
|
||||||
|
|
||||||
@@ -118,7 +115,6 @@ func TestParseDate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateArithmetic(t *testing.T) {
|
func TestDateArithmetic(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
desc string
|
desc string
|
||||||
@@ -180,7 +176,6 @@ func TestDateArithmetic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateBefore(t *testing.T) {
|
func TestDateBefore(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
d1, d2 LocalDate
|
d1, d2 LocalDate
|
||||||
@@ -198,7 +193,6 @@ func TestDateBefore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateAfter(t *testing.T) {
|
func TestDateAfter(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
d1, d2 LocalDate
|
d1, d2 LocalDate
|
||||||
@@ -215,7 +209,6 @@ func TestDateAfter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeToString(t *testing.T) {
|
func TestTimeToString(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
str string
|
str string
|
||||||
@@ -249,7 +242,6 @@ func TestTimeToString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeOf(t *testing.T) {
|
func TestTimeOf(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
time time.Time
|
time time.Time
|
||||||
@@ -265,7 +257,6 @@ func TestTimeOf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeIsValid(t *testing.T) {
|
func TestTimeIsValid(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
time LocalTime
|
time LocalTime
|
||||||
@@ -291,7 +282,6 @@ func TestTimeIsValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateTimeToString(t *testing.T) {
|
func TestDateTimeToString(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
str string
|
str string
|
||||||
@@ -323,7 +313,6 @@ func TestDateTimeToString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseDateTimeErrors(t *testing.T) {
|
func TestParseDateTimeErrors(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, str := range []string{
|
for _, str := range []string{
|
||||||
"",
|
"",
|
||||||
@@ -339,7 +328,6 @@ func TestParseDateTimeErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateTimeOf(t *testing.T) {
|
func TestDateTimeOf(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
time time.Time
|
time time.Time
|
||||||
@@ -361,7 +349,6 @@ func TestDateTimeOf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateTimeIsValid(t *testing.T) {
|
func TestDateTimeIsValid(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// No need to be exhaustive here; it's just LocalDate.IsValid && LocalTime.IsValid.
|
// No need to be exhaustive here; it's just LocalDate.IsValid && LocalTime.IsValid.
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
@@ -380,7 +367,6 @@ func TestDateTimeIsValid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateTimeIn(t *testing.T) {
|
func TestDateTimeIn(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dt := LocalDateTime{LocalDate{2016, 1, 2}, LocalTime{3, 4, 5, 6}}
|
dt := LocalDateTime{LocalDate{2016, 1, 2}, LocalTime{3, 4, 5, 6}}
|
||||||
|
|
||||||
@@ -391,7 +377,6 @@ func TestDateTimeIn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateTimeBefore(t *testing.T) {
|
func TestDateTimeBefore(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
d1 := LocalDate{2016, 12, 31}
|
d1 := LocalDate{2016, 12, 31}
|
||||||
d2 := LocalDate{2017, 1, 1}
|
d2 := LocalDate{2017, 1, 1}
|
||||||
@@ -414,7 +399,6 @@ func TestDateTimeBefore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDateTimeAfter(t *testing.T) {
|
func TestDateTimeAfter(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
d1 := LocalDate{2016, 12, 31}
|
d1 := LocalDate{2016, 12, 31}
|
||||||
d2 := LocalDate{2017, 1, 1}
|
d2 := LocalDate{2017, 1, 1}
|
||||||
@@ -437,7 +421,6 @@ func TestDateTimeAfter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalJSON(t *testing.T) {
|
func TestMarshalJSON(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
value interface{}
|
value interface{}
|
||||||
@@ -459,7 +442,6 @@ func TestMarshalJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalJSON(t *testing.T) {
|
func TestUnmarshalJSON(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
d LocalDate
|
d LocalDate
|
||||||
|
|||||||
@@ -640,8 +640,6 @@ func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte
|
|||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
|
|
||||||
|
|
||||||
func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
|
func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
|
||||||
if v.Type() == timeType || v.Type().Implements(textMarshalerType) {
|
if v.Type() == timeType || v.Type().Implements(textMarshalerType) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import (
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestMarshal(t *testing.T) {
|
func TestMarshal(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
someInt := 42
|
someInt := 42
|
||||||
|
|
||||||
type structInline struct {
|
type structInline struct {
|
||||||
@@ -516,8 +514,6 @@ K = 42`,
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
b, err := toml.Marshal(e.v)
|
b, err := toml.Marshal(e.v)
|
||||||
if e.err {
|
if e.err {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -609,8 +605,6 @@ func equalStringsIgnoreNewlines(t *testing.T, expected string, actual string) {
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestMarshalIndentTables(t *testing.T) {
|
func TestMarshalIndentTables(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
v interface{}
|
v interface{}
|
||||||
@@ -661,8 +655,6 @@ root = 'value0'
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
enc := toml.NewEncoder(&buf)
|
enc := toml.NewEncoder(&buf)
|
||||||
enc.SetIndentTables(true)
|
enc.SetIndentTables(true)
|
||||||
@@ -685,24 +677,18 @@ func (c *customTextMarshaler) MarshalText() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalTextMarshaler_NoRoot(t *testing.T) {
|
func TestMarshalTextMarshaler_NoRoot(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
c := customTextMarshaler{}
|
c := customTextMarshaler{}
|
||||||
_, err := toml.Marshal(&c)
|
_, err := toml.Marshal(&c)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalTextMarshaler_Error(t *testing.T) {
|
func TestMarshalTextMarshaler_Error(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
m := map[string]interface{}{"a": &customTextMarshaler{value: 1}}
|
m := map[string]interface{}{"a": &customTextMarshaler{value: 1}}
|
||||||
_, err := toml.Marshal(m)
|
_, err := toml.Marshal(m)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalTextMarshaler_ErrorInline(t *testing.T) {
|
func TestMarshalTextMarshaler_ErrorInline(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type s struct {
|
type s struct {
|
||||||
A map[string]interface{} `inline:"true"`
|
A map[string]interface{} `inline:"true"`
|
||||||
}
|
}
|
||||||
@@ -716,8 +702,6 @@ func TestMarshalTextMarshaler_ErrorInline(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalTextMarshaler(t *testing.T) {
|
func TestMarshalTextMarshaler(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
m := map[string]interface{}{"a": &customTextMarshaler{value: 2}}
|
m := map[string]interface{}{"a": &customTextMarshaler{value: 2}}
|
||||||
r, err := toml.Marshal(m)
|
r, err := toml.Marshal(m)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -731,7 +715,6 @@ func (b *brokenWriter) Write([]byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeToBrokenWriter(t *testing.T) {
|
func TestEncodeToBrokenWriter(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
w := brokenWriter{}
|
w := brokenWriter{}
|
||||||
enc := toml.NewEncoder(&w)
|
enc := toml.NewEncoder(&w)
|
||||||
err := enc.Encode(map[string]string{"hello": "world"})
|
err := enc.Encode(map[string]string{"hello": "world"})
|
||||||
@@ -739,7 +722,6 @@ func TestEncodeToBrokenWriter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEncoderSetIndentSymbol(t *testing.T) {
|
func TestEncoderSetIndentSymbol(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
var w strings.Builder
|
var w strings.Builder
|
||||||
enc := toml.NewEncoder(&w)
|
enc := toml.NewEncoder(&w)
|
||||||
enc.SetIndentTables(true)
|
enc.SetIndentTables(true)
|
||||||
@@ -753,8 +735,6 @@ func TestEncoderSetIndentSymbol(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue436(t *testing.T) {
|
func TestIssue436(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
data := []byte(`{"a": [ { "b": { "c": "d" } } ]}`)
|
data := []byte(`{"a": [ { "b": { "c": "d" } } ]}`)
|
||||||
|
|
||||||
var v interface{}
|
var v interface{}
|
||||||
@@ -774,8 +754,6 @@ c = 'd'
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue424(t *testing.T) {
|
func TestIssue424(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type Message1 struct {
|
type Message1 struct {
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestParser_AST_Numbers(t *testing.T) {
|
func TestParser_AST_Numbers(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
@@ -137,7 +136,7 @@ func TestParser_AST_Numbers(t *testing.T) {
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
p := parser{}
|
p := parser{}
|
||||||
p.Reset([]byte(`A = ` + e.input))
|
p.Reset([]byte(`A = ` + e.input))
|
||||||
p.NextExpression()
|
p.NextExpression()
|
||||||
@@ -200,7 +199,6 @@ func compareIterator(t *testing.T, expected []astNode, actual ast.Iterator) {
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestParser_AST(t *testing.T) {
|
func TestParser_AST(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
@@ -340,7 +338,7 @@ func TestParser_AST(t *testing.T) {
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
p := parser{}
|
p := parser{}
|
||||||
p.Reset([]byte(e.input))
|
p.Reset([]byte(e.input))
|
||||||
p.NextExpression()
|
p.NextExpression()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package toml
|
|||||||
import (
|
import (
|
||||||
"github.com/pelletier/go-toml/v2/internal/ast"
|
"github.com/pelletier/go-toml/v2/internal/ast"
|
||||||
"github.com/pelletier/go-toml/v2/internal/tracker"
|
"github.com/pelletier/go-toml/v2/internal/tracker"
|
||||||
|
"github.com/pelletier/go-toml/v2/internal/unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
type strict struct {
|
type strict struct {
|
||||||
@@ -86,3 +87,21 @@ func (s *strict) Error(doc []byte) error {
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keyLocation(node ast.Node) []byte {
|
||||||
|
k := node.Key()
|
||||||
|
|
||||||
|
hasOne := k.Next()
|
||||||
|
if !hasOne {
|
||||||
|
panic("should not be called with empty key")
|
||||||
|
}
|
||||||
|
|
||||||
|
start := k.Node().Data
|
||||||
|
end := k.Node().Data
|
||||||
|
|
||||||
|
for k.Next() {
|
||||||
|
end = k.Node().Data
|
||||||
|
}
|
||||||
|
|
||||||
|
return unsafe.BytesRange(start, end)
|
||||||
|
}
|
||||||
|
|||||||
-536
@@ -1,536 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type target interface {
|
|
||||||
// Dereferences the target.
|
|
||||||
get() reflect.Value
|
|
||||||
|
|
||||||
// Store a string at the target.
|
|
||||||
setString(v string)
|
|
||||||
|
|
||||||
// Store a boolean at the target
|
|
||||||
setBool(v bool)
|
|
||||||
|
|
||||||
// Store an int64 at the target
|
|
||||||
setInt64(v int64)
|
|
||||||
|
|
||||||
// Store a float64 at the target
|
|
||||||
setFloat64(v float64)
|
|
||||||
|
|
||||||
// Stores any value at the target
|
|
||||||
set(v reflect.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// valueTarget just contains a reflect.Value that can be set.
|
|
||||||
// It is used for struct fields.
|
|
||||||
type valueTarget reflect.Value
|
|
||||||
|
|
||||||
func (t valueTarget) get() reflect.Value {
|
|
||||||
return reflect.Value(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t valueTarget) set(v reflect.Value) {
|
|
||||||
reflect.Value(t).Set(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t valueTarget) setString(v string) {
|
|
||||||
t.get().SetString(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t valueTarget) setBool(v bool) {
|
|
||||||
t.get().SetBool(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t valueTarget) setInt64(v int64) {
|
|
||||||
t.get().SetInt(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t valueTarget) setFloat64(v float64) {
|
|
||||||
t.get().SetFloat(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// interfaceTarget wraps an other target to dereference on get.
|
|
||||||
type interfaceTarget struct {
|
|
||||||
x target
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t interfaceTarget) get() reflect.Value {
|
|
||||||
return t.x.get().Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t interfaceTarget) set(v reflect.Value) {
|
|
||||||
t.x.set(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t interfaceTarget) setString(v string) {
|
|
||||||
panic("interface targets should always go through set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t interfaceTarget) setBool(v bool) {
|
|
||||||
panic("interface targets should always go through set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t interfaceTarget) setInt64(v int64) {
|
|
||||||
panic("interface targets should always go through set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t interfaceTarget) setFloat64(v float64) {
|
|
||||||
panic("interface targets should always go through set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// mapTarget targets a specific key of a map.
|
|
||||||
type mapTarget struct {
|
|
||||||
v reflect.Value
|
|
||||||
k reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t mapTarget) get() reflect.Value {
|
|
||||||
return t.v.MapIndex(t.k)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t mapTarget) set(v reflect.Value) {
|
|
||||||
t.v.SetMapIndex(t.k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t mapTarget) setString(v string) {
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t mapTarget) setBool(v bool) {
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t mapTarget) setInt64(v int64) {
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t mapTarget) setFloat64(v float64) {
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes sure that the value pointed at by t is indexable (Slice, Array), or
|
|
||||||
// dereferences to an indexable (Ptr, Interface).
|
|
||||||
func ensureValueIndexable(t target) error {
|
|
||||||
f := t.get()
|
|
||||||
|
|
||||||
switch f.Type().Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
if f.IsNil() {
|
|
||||||
t.set(reflect.MakeSlice(f.Type(), 0, 0))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
if f.IsNil() || f.Elem().Type() != sliceInterfaceType {
|
|
||||||
t.set(reflect.MakeSlice(sliceInterfaceType, 0, 0))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
panic("pointer should have already been dereferenced")
|
|
||||||
case reflect.Array:
|
|
||||||
// arrays are always initialized.
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("toml: cannot store array in a %s", f.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
sliceInterfaceType = reflect.TypeOf([]interface{}{})
|
|
||||||
mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
|
|
||||||
)
|
|
||||||
|
|
||||||
func ensureMapIfInterface(x target) {
|
|
||||||
v := x.get()
|
|
||||||
|
|
||||||
if v.Kind() == reflect.Interface && v.IsNil() {
|
|
||||||
newElement := reflect.MakeMap(mapStringInterfaceType)
|
|
||||||
|
|
||||||
x.set(newElement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setString(t target, v string) error {
|
|
||||||
f := t.get()
|
|
||||||
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
t.setString(v)
|
|
||||||
case reflect.Interface:
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("toml: cannot assign string to a %s", f.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setBool(t target, v bool) error {
|
|
||||||
f := t.get()
|
|
||||||
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
t.setBool(v)
|
|
||||||
case reflect.Interface:
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("toml: cannot assign boolean to a %s", f.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxInt = int64(^uint(0) >> 1)
|
|
||||||
minInt = -maxInt - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint:funlen,gocognit,cyclop
|
|
||||||
func setInt64(t target, v int64) error {
|
|
||||||
f := t.get()
|
|
||||||
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Int64:
|
|
||||||
t.setInt64(v)
|
|
||||||
case reflect.Int32:
|
|
||||||
if v < math.MinInt32 || v > math.MaxInt32 {
|
|
||||||
return fmt.Errorf("toml: number %d does not fit in an int32", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(int32(v)))
|
|
||||||
return nil
|
|
||||||
case reflect.Int16:
|
|
||||||
if v < math.MinInt16 || v > math.MaxInt16 {
|
|
||||||
return fmt.Errorf("toml: number %d does not fit in an int16", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(int16(v)))
|
|
||||||
case reflect.Int8:
|
|
||||||
if v < math.MinInt8 || v > math.MaxInt8 {
|
|
||||||
return fmt.Errorf("toml: number %d does not fit in an int8", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(int8(v)))
|
|
||||||
case reflect.Int:
|
|
||||||
if v < minInt || v > maxInt {
|
|
||||||
return fmt.Errorf("toml: number %d does not fit in an int", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(int(v)))
|
|
||||||
case reflect.Uint64:
|
|
||||||
if v < 0 {
|
|
||||||
return fmt.Errorf("toml: negative number %d does not fit in an uint64", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(uint64(v)))
|
|
||||||
case reflect.Uint32:
|
|
||||||
if v < 0 || v > math.MaxUint32 {
|
|
||||||
return fmt.Errorf("toml: negative number %d does not fit in an uint32", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(uint32(v)))
|
|
||||||
case reflect.Uint16:
|
|
||||||
if v < 0 || v > math.MaxUint16 {
|
|
||||||
return fmt.Errorf("toml: negative number %d does not fit in an uint16", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(uint16(v)))
|
|
||||||
case reflect.Uint8:
|
|
||||||
if v < 0 || v > math.MaxUint8 {
|
|
||||||
return fmt.Errorf("toml: negative number %d does not fit in an uint8", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(uint8(v)))
|
|
||||||
case reflect.Uint:
|
|
||||||
if v < 0 {
|
|
||||||
return fmt.Errorf("toml: negative number %d does not fit in an uint", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(uint(v)))
|
|
||||||
case reflect.Interface:
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("toml: integer cannot be assigned to %s", f.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setFloat64(t target, v float64) error {
|
|
||||||
f := t.get()
|
|
||||||
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Float64:
|
|
||||||
t.setFloat64(v)
|
|
||||||
case reflect.Float32:
|
|
||||||
if v > math.MaxFloat32 {
|
|
||||||
return fmt.Errorf("toml: number %f does not fit in a float32", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.ValueOf(float32(v)))
|
|
||||||
case reflect.Interface:
|
|
||||||
t.set(reflect.ValueOf(v))
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("toml: float cannot be assigned to %s", f.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the element at idx of the value pointed at by target, or an error if
|
|
||||||
// t does not point to an indexable.
|
|
||||||
// If the target points to an Array and idx is out of bounds, it returns
|
|
||||||
// (nil, nil) as this is not a fatal error (the unmarshaler will skip).
|
|
||||||
func elementAt(t target, idx int) target {
|
|
||||||
f := t.get()
|
|
||||||
|
|
||||||
switch f.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
//nolint:godox
|
|
||||||
// TODO: use the idx function argument and avoid alloc if possible.
|
|
||||||
idx := f.Len()
|
|
||||||
|
|
||||||
t.set(reflect.Append(f, reflect.New(f.Type().Elem()).Elem()))
|
|
||||||
|
|
||||||
return valueTarget(t.get().Index(idx))
|
|
||||||
case reflect.Array:
|
|
||||||
if idx >= f.Len() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueTarget(f.Index(idx))
|
|
||||||
case reflect.Interface:
|
|
||||||
// This function is called after ensureValueIndexable, so it's
|
|
||||||
// guaranteed that f contains an initialized slice.
|
|
||||||
ifaceElem := f.Elem()
|
|
||||||
idx := ifaceElem.Len()
|
|
||||||
newElem := reflect.New(ifaceElem.Type().Elem()).Elem()
|
|
||||||
newSlice := reflect.Append(ifaceElem, newElem)
|
|
||||||
|
|
||||||
t.set(newSlice)
|
|
||||||
|
|
||||||
return valueTarget(t.get().Elem().Index(idx))
|
|
||||||
default:
|
|
||||||
// Why ensureValueIndexable let it go through?
|
|
||||||
panic(fmt.Errorf("elementAt received unhandled value type: %s", f.Kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) scopeTableTarget(shouldAppend bool, t target, name string) (target, bool, error) {
|
|
||||||
x := t.get()
|
|
||||||
|
|
||||||
switch x.Kind() {
|
|
||||||
// Kinds that need to recurse
|
|
||||||
case reflect.Interface:
|
|
||||||
t := scopeInterface(shouldAppend, t)
|
|
||||||
return d.scopeTableTarget(shouldAppend, t, name)
|
|
||||||
case reflect.Ptr:
|
|
||||||
t := scopePtr(t)
|
|
||||||
return d.scopeTableTarget(shouldAppend, t, name)
|
|
||||||
case reflect.Slice:
|
|
||||||
t := scopeSlice(shouldAppend, t)
|
|
||||||
shouldAppend = false
|
|
||||||
return d.scopeTableTarget(shouldAppend, t, name)
|
|
||||||
case reflect.Array:
|
|
||||||
t, err := d.scopeArray(shouldAppend, t)
|
|
||||||
if err != nil {
|
|
||||||
return t, false, err
|
|
||||||
}
|
|
||||||
shouldAppend = false
|
|
||||||
|
|
||||||
return d.scopeTableTarget(shouldAppend, t, name)
|
|
||||||
|
|
||||||
// Terminal kinds
|
|
||||||
case reflect.Struct:
|
|
||||||
return scopeStruct(x, name)
|
|
||||||
case reflect.Map:
|
|
||||||
if x.IsNil() {
|
|
||||||
t.set(reflect.MakeMap(x.Type()))
|
|
||||||
x = t.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
return scopeMap(x, name)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("can't scope on a %s", x.Kind()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopeInterface(shouldAppend bool, t target) target {
|
|
||||||
initInterface(shouldAppend, t)
|
|
||||||
return interfaceTarget{t}
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopePtr(t target) target {
|
|
||||||
initPtr(t)
|
|
||||||
return valueTarget(t.get().Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
func initPtr(t target) {
|
|
||||||
x := t.get()
|
|
||||||
if !x.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(reflect.New(x.Type().Elem()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// initInterface makes sure that the interface pointed at by the target is not
|
|
||||||
// nil.
|
|
||||||
// Returns the target to the initialized value of the target.
|
|
||||||
func initInterface(shouldAppend bool, t target) {
|
|
||||||
x := t.get()
|
|
||||||
|
|
||||||
if x.Kind() != reflect.Interface {
|
|
||||||
panic("this should only be called on interfaces")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !x.IsNil() && (x.Elem().Type() == sliceInterfaceType || x.Elem().Type() == mapStringInterfaceType) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var newElement reflect.Value
|
|
||||||
if shouldAppend {
|
|
||||||
newElement = reflect.MakeSlice(sliceInterfaceType, 0, 0)
|
|
||||||
} else {
|
|
||||||
newElement = reflect.MakeMap(mapStringInterfaceType)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.set(newElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopeSlice(shouldAppend bool, t target) target {
|
|
||||||
v := t.get()
|
|
||||||
|
|
||||||
if shouldAppend {
|
|
||||||
newElem := reflect.New(v.Type().Elem())
|
|
||||||
newSlice := reflect.Append(v, newElem.Elem())
|
|
||||||
|
|
||||||
t.set(newSlice)
|
|
||||||
|
|
||||||
v = t.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueTarget(v.Index(v.Len() - 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) scopeArray(shouldAppend bool, t target) (target, error) {
|
|
||||||
v := t.get()
|
|
||||||
|
|
||||||
idx := d.arrayIndex(shouldAppend, v)
|
|
||||||
|
|
||||||
if idx >= v.Len() {
|
|
||||||
return nil, fmt.Errorf("toml: impossible to insert element beyond array's size: %d", v.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueTarget(v.Index(idx)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopeMap(v reflect.Value, name string) (target, bool, error) {
|
|
||||||
k := reflect.ValueOf(name)
|
|
||||||
|
|
||||||
keyType := v.Type().Key()
|
|
||||||
if !k.Type().AssignableTo(keyType) {
|
|
||||||
if !k.Type().ConvertibleTo(keyType) {
|
|
||||||
return nil, false, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", k.Type(), keyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
k = k.Convert(keyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.MapIndex(k).IsValid() {
|
|
||||||
newElem := reflect.New(v.Type().Elem())
|
|
||||||
v.SetMapIndex(k, newElem.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapTarget{
|
|
||||||
v: v,
|
|
||||||
k: k,
|
|
||||||
}, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldPathsMap = map[string][]int
|
|
||||||
|
|
||||||
type fieldPathsCache struct {
|
|
||||||
m map[reflect.Type]fieldPathsMap
|
|
||||||
l sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fieldPathsCache) get(t reflect.Type) (fieldPathsMap, bool) {
|
|
||||||
c.l.RLock()
|
|
||||||
paths, ok := c.m[t]
|
|
||||||
c.l.RUnlock()
|
|
||||||
|
|
||||||
return paths, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fieldPathsCache) set(t reflect.Type, m fieldPathsMap) {
|
|
||||||
c.l.Lock()
|
|
||||||
c.m[t] = m
|
|
||||||
c.l.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
var globalFieldPathsCache = fieldPathsCache{
|
|
||||||
m: map[reflect.Type]fieldPathsMap{},
|
|
||||||
l: sync.RWMutex{},
|
|
||||||
}
|
|
||||||
|
|
||||||
func scopeStruct(v reflect.Value, name string) (target, bool, error) {
|
|
||||||
//nolint:godox
|
|
||||||
// TODO: cache this, and reduce allocations
|
|
||||||
fieldPaths, ok := globalFieldPathsCache.get(v.Type())
|
|
||||||
if !ok {
|
|
||||||
fieldPaths = map[string][]int{}
|
|
||||||
|
|
||||||
path := make([]int, 0, 16)
|
|
||||||
|
|
||||||
var walk func(reflect.Value)
|
|
||||||
walk = func(v reflect.Value) {
|
|
||||||
t := v.Type()
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
l := len(path)
|
|
||||||
path = append(path, i)
|
|
||||||
f := t.Field(i)
|
|
||||||
|
|
||||||
if f.Anonymous {
|
|
||||||
walk(v.Field(i))
|
|
||||||
} else if f.PkgPath == "" {
|
|
||||||
// only consider exported fields
|
|
||||||
fieldName, ok := f.Tag.Lookup("toml")
|
|
||||||
if !ok {
|
|
||||||
fieldName = f.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
pathCopy := make([]int, len(path))
|
|
||||||
copy(pathCopy, path)
|
|
||||||
|
|
||||||
fieldPaths[fieldName] = pathCopy
|
|
||||||
// extra copy for the case-insensitive match
|
|
||||||
fieldPaths[strings.ToLower(fieldName)] = pathCopy
|
|
||||||
}
|
|
||||||
path = path[:l]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
walk(v)
|
|
||||||
|
|
||||||
globalFieldPathsCache.set(v.Type(), fieldPaths)
|
|
||||||
}
|
|
||||||
|
|
||||||
path, ok := fieldPaths[name]
|
|
||||||
if !ok {
|
|
||||||
path, ok = fieldPaths[strings.ToLower(name)]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueTarget(v.FieldByIndex(path)), true, nil
|
|
||||||
}
|
|
||||||
-207
@@ -1,207 +0,0 @@
|
|||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStructTarget_Ensure(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
|
||||||
desc string
|
|
||||||
input reflect.Value
|
|
||||||
name string
|
|
||||||
test func(v reflect.Value, err error)
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "handle a nil slice of string",
|
|
||||||
input: reflect.ValueOf(&struct{ A []string }{}).Elem(),
|
|
||||||
name: "A",
|
|
||||||
test: func(v reflect.Value, err error) {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.False(t, v.IsNil())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "handle an existing slice of string",
|
|
||||||
input: reflect.ValueOf(&struct{ A []string }{A: []string{"foo"}}).Elem(),
|
|
||||||
name: "A",
|
|
||||||
test: func(v reflect.Value, err error) {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
require.False(t, v.IsNil())
|
|
||||||
|
|
||||||
s, ok := v.Interface().([]string)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("interface %v should be castable into []string", s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, []string{"foo"}, s)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range examples {
|
|
||||||
e := e
|
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
d := decoder{}
|
|
||||||
target, _, err := d.scopeTableTarget(false, valueTarget(e.input), e.name)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ensureValueIndexable(target)
|
|
||||||
v := target.get()
|
|
||||||
e.test(v, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStructTarget_SetString(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
str := "value"
|
|
||||||
|
|
||||||
examples := []struct {
|
|
||||||
desc string
|
|
||||||
input reflect.Value
|
|
||||||
name string
|
|
||||||
test func(v reflect.Value, err error)
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "sets a string",
|
|
||||||
input: reflect.ValueOf(&struct{ A string }{}).Elem(),
|
|
||||||
name: "A",
|
|
||||||
test: func(v reflect.Value, err error) {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, str, v.String())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "fails on a float",
|
|
||||||
input: reflect.ValueOf(&struct{ A float64 }{}).Elem(),
|
|
||||||
name: "A",
|
|
||||||
test: func(v reflect.Value, err error) {
|
|
||||||
assert.Error(t, err)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "fails on a slice",
|
|
||||||
input: reflect.ValueOf(&struct{ A []string }{}).Elem(),
|
|
||||||
name: "A",
|
|
||||||
test: func(v reflect.Value, err error) {
|
|
||||||
assert.Error(t, err)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range examples {
|
|
||||||
e := e
|
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
d := decoder{}
|
|
||||||
target, _, err := d.scopeTableTarget(false, valueTarget(e.input), e.name)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = setString(target, str)
|
|
||||||
v := target.get()
|
|
||||||
e.test(v, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPushNew(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
t.Run("slice of strings", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type Doc struct {
|
|
||||||
A []string
|
|
||||||
}
|
|
||||||
d := Doc{}
|
|
||||||
|
|
||||||
dec := decoder{}
|
|
||||||
x, _, err := dec.scopeTableTarget(false, valueTarget(reflect.ValueOf(&d).Elem()), "A")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
n := elementAt(x, 0)
|
|
||||||
n.setString("hello")
|
|
||||||
require.Equal(t, []string{"hello"}, d.A)
|
|
||||||
|
|
||||||
n = elementAt(x, 1)
|
|
||||||
n.setString("world")
|
|
||||||
require.Equal(t, []string{"hello", "world"}, d.A)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("slice of interfaces", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type Doc struct {
|
|
||||||
A []interface{}
|
|
||||||
}
|
|
||||||
d := Doc{}
|
|
||||||
|
|
||||||
dec := decoder{}
|
|
||||||
x, _, err := dec.scopeTableTarget(false, valueTarget(reflect.ValueOf(&d).Elem()), "A")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
n := elementAt(x, 0)
|
|
||||||
require.NoError(t, setString(n, "hello"))
|
|
||||||
require.Equal(t, []interface{}{"hello"}, d.A)
|
|
||||||
|
|
||||||
n = elementAt(x, 1)
|
|
||||||
require.NoError(t, setString(n, "world"))
|
|
||||||
require.Equal(t, []interface{}{"hello", "world"}, d.A)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScope_Struct(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
|
||||||
desc string
|
|
||||||
input reflect.Value
|
|
||||||
name string
|
|
||||||
err bool
|
|
||||||
found bool
|
|
||||||
idx []int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "simple field",
|
|
||||||
input: reflect.ValueOf(&struct{ A string }{}).Elem(),
|
|
||||||
name: "A",
|
|
||||||
idx: []int{0},
|
|
||||||
found: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "fails not-exported field",
|
|
||||||
input: reflect.ValueOf(&struct{ a string }{}).Elem(),
|
|
||||||
name: "a",
|
|
||||||
err: false,
|
|
||||||
found: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range examples {
|
|
||||||
e := e
|
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dec := decoder{}
|
|
||||||
x, found, err := dec.scopeTableTarget(false, valueTarget(e.input), e.name)
|
|
||||||
assert.Equal(t, e.found, found)
|
|
||||||
if e.err {
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
if found {
|
|
||||||
x2, ok := x.(valueTarget)
|
|
||||||
require.True(t, ok)
|
|
||||||
x2.get()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,35 +6,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInvalidDatetimeMalformedNoLeads(t *testing.T) {
|
func TestInvalidDatetimeMalformedNoLeads(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `no-leads = 1987-7-05T17:45:00Z`
|
input := `no-leads = 1987-7-05T17:45:00Z`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidDatetimeMalformedNoSecs(t *testing.T) {
|
func TestInvalidDatetimeMalformedNoSecs(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `no-secs = 1987-07-05T17:45Z`
|
input := `no-secs = 1987-07-05T17:45Z`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidDatetimeMalformedNoT(t *testing.T) {
|
func TestInvalidDatetimeMalformedNoT(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `no-t = 1987-07-0517:45:00Z`
|
input := `no-t = 1987-07-0517:45:00Z`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidDatetimeMalformedWithMilli(t *testing.T) {
|
func TestInvalidDatetimeMalformedWithMilli(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `with-milli = 1987-07-5T17:45:00.12Z`
|
input := `with-milli = 1987-07-5T17:45:00.12Z`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidDuplicateKeyTable(t *testing.T) {
|
func TestInvalidDuplicateKeyTable(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[fruit]
|
input := `[fruit]
|
||||||
type = "apple"
|
type = "apple"
|
||||||
@@ -45,7 +40,6 @@ apple = "yes"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidDuplicateKeys(t *testing.T) {
|
func TestInvalidDuplicateKeys(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `dupe = false
|
input := `dupe = false
|
||||||
dupe = true`
|
dupe = true`
|
||||||
@@ -53,7 +47,6 @@ dupe = true`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidDuplicateTables(t *testing.T) {
|
func TestInvalidDuplicateTables(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a]
|
input := `[a]
|
||||||
[a]`
|
[a]`
|
||||||
@@ -61,21 +54,18 @@ func TestInvalidDuplicateTables(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidEmptyImplicitTable(t *testing.T) {
|
func TestInvalidEmptyImplicitTable(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[naughty..naughty]`
|
input := `[naughty..naughty]`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidEmptyTable(t *testing.T) {
|
func TestInvalidEmptyTable(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[]`
|
input := `[]`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidFloatNoLeadingZero(t *testing.T) {
|
func TestInvalidFloatNoLeadingZero(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = .12345
|
input := `answer = .12345
|
||||||
neganswer = -.12345`
|
neganswer = -.12345`
|
||||||
@@ -83,7 +73,6 @@ neganswer = -.12345`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidFloatNoTrailingDigits(t *testing.T) {
|
func TestInvalidFloatNoTrailingDigits(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = 1.
|
input := `answer = 1.
|
||||||
neganswer = -1.`
|
neganswer = -1.`
|
||||||
@@ -91,21 +80,18 @@ neganswer = -1.`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeyEmpty(t *testing.T) {
|
func TestInvalidKeyEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := ` = 1`
|
input := ` = 1`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeyHash(t *testing.T) {
|
func TestInvalidKeyHash(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `a# = 1`
|
input := `a# = 1`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeyNewline(t *testing.T) {
|
func TestInvalidKeyNewline(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `a
|
input := `a
|
||||||
= 1`
|
= 1`
|
||||||
@@ -113,28 +99,24 @@ func TestInvalidKeyNewline(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeyOpenBracket(t *testing.T) {
|
func TestInvalidKeyOpenBracket(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[abc = 1`
|
input := `[abc = 1`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeySingleOpenBracket(t *testing.T) {
|
func TestInvalidKeySingleOpenBracket(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[`
|
input := `[`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeySpace(t *testing.T) {
|
func TestInvalidKeySpace(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `a b = 1`
|
input := `a b = 1`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeyStartBracket(t *testing.T) {
|
func TestInvalidKeyStartBracket(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a]
|
input := `[a]
|
||||||
[xyz = 5
|
[xyz = 5
|
||||||
@@ -143,42 +125,36 @@ func TestInvalidKeyStartBracket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidKeyTwoEquals(t *testing.T) {
|
func TestInvalidKeyTwoEquals(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `key= = 1`
|
input := `key= = 1`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidStringBadByteEscape(t *testing.T) {
|
func TestInvalidStringBadByteEscape(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `naughty = "\xAg"`
|
input := `naughty = "\xAg"`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidStringBadEscape(t *testing.T) {
|
func TestInvalidStringBadEscape(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `invalid-escape = "This string has a bad \a escape character."`
|
input := `invalid-escape = "This string has a bad \a escape character."`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidStringByteEscapes(t *testing.T) {
|
func TestInvalidStringByteEscapes(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = "\x33"`
|
input := `answer = "\x33"`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidStringNoClose(t *testing.T) {
|
func TestInvalidStringNoClose(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `no-ending-quote = "One time, at band camp`
|
input := `no-ending-quote = "One time, at band camp`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableArrayImplicit(t *testing.T) {
|
func TestInvalidTableArrayImplicit(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := "# This test is a bit tricky. It should fail because the first use of\n" +
|
input := "# This test is a bit tricky. It should fail because the first use of\n" +
|
||||||
"# `[[albums.songs]]` without first declaring `albums` implies that `albums`\n" +
|
"# `[[albums.songs]]` without first declaring `albums` implies that `albums`\n" +
|
||||||
@@ -198,7 +174,6 @@ func TestInvalidTableArrayImplicit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableArrayMalformedBracket(t *testing.T) {
|
func TestInvalidTableArrayMalformedBracket(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[[albums]
|
input := `[[albums]
|
||||||
name = "Born to Run"`
|
name = "Born to Run"`
|
||||||
@@ -206,7 +181,6 @@ name = "Born to Run"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableArrayMalformedEmpty(t *testing.T) {
|
func TestInvalidTableArrayMalformedEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[[]]
|
input := `[[]]
|
||||||
name = "Born to Run"`
|
name = "Born to Run"`
|
||||||
@@ -214,14 +188,12 @@ name = "Born to Run"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableEmpty(t *testing.T) {
|
func TestInvalidTableEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[]`
|
input := `[]`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableNestedBracketsClose(t *testing.T) {
|
func TestInvalidTableNestedBracketsClose(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a]b]
|
input := `[a]b]
|
||||||
zyx = 42`
|
zyx = 42`
|
||||||
@@ -229,7 +201,6 @@ zyx = 42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableNestedBracketsOpen(t *testing.T) {
|
func TestInvalidTableNestedBracketsOpen(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a[b]
|
input := `[a[b]
|
||||||
zyx = 42`
|
zyx = 42`
|
||||||
@@ -237,14 +208,12 @@ zyx = 42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableWhitespace(t *testing.T) {
|
func TestInvalidTableWhitespace(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[invalid key]`
|
input := `[invalid key]`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTableWithPound(t *testing.T) {
|
func TestInvalidTableWithPound(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[key#group]
|
input := `[key#group]
|
||||||
answer = 42`
|
answer = 42`
|
||||||
@@ -252,7 +221,6 @@ answer = 42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTextAfterArrayEntries(t *testing.T) {
|
func TestInvalidTextAfterArrayEntries(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `array = [
|
input := `array = [
|
||||||
"Is there life after an array separator?", No
|
"Is there life after an array separator?", No
|
||||||
@@ -262,28 +230,24 @@ func TestInvalidTextAfterArrayEntries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTextAfterInteger(t *testing.T) {
|
func TestInvalidTextAfterInteger(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = 42 the ultimate answer?`
|
input := `answer = 42 the ultimate answer?`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTextAfterString(t *testing.T) {
|
func TestInvalidTextAfterString(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `string = "Is there life after strings?" No.`
|
input := `string = "Is there life after strings?" No.`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTextAfterTable(t *testing.T) {
|
func TestInvalidTextAfterTable(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[error] this shouldn't be here`
|
input := `[error] this shouldn't be here`
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTextBeforeArraySeparator(t *testing.T) {
|
func TestInvalidTextBeforeArraySeparator(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `array = [
|
input := `array = [
|
||||||
"Is there life before an array separator?" No,
|
"Is there life before an array separator?" No,
|
||||||
@@ -293,7 +257,6 @@ func TestInvalidTextBeforeArraySeparator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidTextInArray(t *testing.T) {
|
func TestInvalidTextInArray(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `array = [
|
input := `array = [
|
||||||
"Entry 1",
|
"Entry 1",
|
||||||
@@ -304,7 +267,6 @@ func TestInvalidTextInArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidArrayEmpty(t *testing.T) {
|
func TestValidArrayEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `thevoid = [[[[[]]]]]`
|
input := `thevoid = [[[[[]]]]]`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -322,7 +284,6 @@ func TestValidArrayEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidArrayNospaces(t *testing.T) {
|
func TestValidArrayNospaces(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `ints = [1,2,3]`
|
input := `ints = [1,2,3]`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -339,7 +300,6 @@ func TestValidArrayNospaces(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidArraysHetergeneous(t *testing.T) {
|
func TestValidArraysHetergeneous(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `mixed = [[1, 2], ["a", "b"], [1.1, 2.1]]`
|
input := `mixed = [[1, 2], ["a", "b"], [1.1, 2.1]]`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -365,7 +325,6 @@ func TestValidArraysHetergeneous(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidArraysNested(t *testing.T) {
|
func TestValidArraysNested(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `nest = [["a"], ["b"]]`
|
input := `nest = [["a"], ["b"]]`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -385,7 +344,6 @@ func TestValidArraysNested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidArrays(t *testing.T) {
|
func TestValidArrays(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `ints = [1, 2, 3]
|
input := `ints = [1, 2, 3]
|
||||||
floats = [1.1, 2.1, 3.1]
|
floats = [1.1, 2.1, 3.1]
|
||||||
@@ -433,7 +391,6 @@ dates = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidBool(t *testing.T) {
|
func TestValidBool(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `t = true
|
input := `t = true
|
||||||
f = false`
|
f = false`
|
||||||
@@ -445,7 +402,6 @@ f = false`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidCommentsEverywhere(t *testing.T) {
|
func TestValidCommentsEverywhere(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `# Top comment.
|
input := `# Top comment.
|
||||||
# Top comment.
|
# Top comment.
|
||||||
@@ -487,7 +443,6 @@ more = [ # Comment
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidDatetime(t *testing.T) {
|
func TestValidDatetime(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `bestdayever = 1987-07-05T17:45:00Z`
|
input := `bestdayever = 1987-07-05T17:45:00Z`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -497,7 +452,6 @@ func TestValidDatetime(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidEmpty(t *testing.T) {
|
func TestValidEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := ``
|
input := ``
|
||||||
jsonRef := `{}`
|
jsonRef := `{}`
|
||||||
@@ -505,7 +459,6 @@ func TestValidEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidExample(t *testing.T) {
|
func TestValidExample(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `best-day-ever = 1987-07-05T17:45:00Z
|
input := `best-day-ever = 1987-07-05T17:45:00Z
|
||||||
|
|
||||||
@@ -530,7 +483,6 @@ perfection = [6, 28, 496]`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidFloat(t *testing.T) {
|
func TestValidFloat(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `pi = 3.14
|
input := `pi = 3.14
|
||||||
negpi = -3.14`
|
negpi = -3.14`
|
||||||
@@ -542,7 +494,6 @@ negpi = -3.14`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidImplicitAndExplicitAfter(t *testing.T) {
|
func TestValidImplicitAndExplicitAfter(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a.b.c]
|
input := `[a.b.c]
|
||||||
answer = 42
|
answer = 42
|
||||||
@@ -563,7 +514,6 @@ better = 43`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidImplicitAndExplicitBefore(t *testing.T) {
|
func TestValidImplicitAndExplicitBefore(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a]
|
input := `[a]
|
||||||
better = 43
|
better = 43
|
||||||
@@ -584,7 +534,6 @@ answer = 42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidImplicitGroups(t *testing.T) {
|
func TestValidImplicitGroups(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a.b.c]
|
input := `[a.b.c]
|
||||||
answer = 42`
|
answer = 42`
|
||||||
@@ -601,7 +550,6 @@ answer = 42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidInteger(t *testing.T) {
|
func TestValidInteger(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = 42
|
input := `answer = 42
|
||||||
neganswer = -42`
|
neganswer = -42`
|
||||||
@@ -613,7 +561,6 @@ neganswer = -42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidKeyEqualsNospace(t *testing.T) {
|
func TestValidKeyEqualsNospace(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer=42`
|
input := `answer=42`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -623,7 +570,6 @@ func TestValidKeyEqualsNospace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidKeySpace(t *testing.T) {
|
func TestValidKeySpace(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `"a b" = 1`
|
input := `"a b" = 1`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -633,7 +579,6 @@ func TestValidKeySpace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidKeySpecialChars(t *testing.T) {
|
func TestValidKeySpecialChars(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := "\"~!@$^&*()_+-`1234567890[]|/?><.,;:'\" = 1\n"
|
input := "\"~!@$^&*()_+-`1234567890[]|/?><.,;:'\" = 1\n"
|
||||||
jsonRef := "{\n" +
|
jsonRef := "{\n" +
|
||||||
@@ -645,7 +590,6 @@ func TestValidKeySpecialChars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidLongFloat(t *testing.T) {
|
func TestValidLongFloat(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `longpi = 3.141592653589793
|
input := `longpi = 3.141592653589793
|
||||||
neglongpi = -3.141592653589793`
|
neglongpi = -3.141592653589793`
|
||||||
@@ -657,7 +601,6 @@ neglongpi = -3.141592653589793`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidLongInteger(t *testing.T) {
|
func TestValidLongInteger(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = 9223372036854775807
|
input := `answer = 9223372036854775807
|
||||||
neganswer = -9223372036854775808`
|
neganswer = -9223372036854775808`
|
||||||
@@ -669,7 +612,6 @@ neganswer = -9223372036854775808`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidMultilineString(t *testing.T) {
|
func TestValidMultilineString(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `multiline_empty_one = """"""
|
input := `multiline_empty_one = """"""
|
||||||
multiline_empty_two = """
|
multiline_empty_two = """
|
||||||
@@ -728,7 +670,6 @@ equivalent_three = """\
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidRawMultilineString(t *testing.T) {
|
func TestValidRawMultilineString(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `oneline = '''This string has a ' quote character.'''
|
input := `oneline = '''This string has a ' quote character.'''
|
||||||
firstnl = '''
|
firstnl = '''
|
||||||
@@ -757,7 +698,6 @@ in it.'''`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidRawString(t *testing.T) {
|
func TestValidRawString(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `backspace = 'This string has a \b backspace character.'
|
input := `backspace = 'This string has a \b backspace character.'
|
||||||
tab = 'This string has a \t tab character.'
|
tab = 'This string has a \t tab character.'
|
||||||
@@ -800,7 +740,6 @@ backslash = 'This string has a \\ backslash character.'`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidStringEmpty(t *testing.T) {
|
func TestValidStringEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = ""`
|
input := `answer = ""`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -813,7 +752,6 @@ func TestValidStringEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidStringEscapes(t *testing.T) {
|
func TestValidStringEscapes(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `backspace = "This string has a \b backspace character."
|
input := `backspace = "This string has a \b backspace character."
|
||||||
tab = "This string has a \t tab character."
|
tab = "This string has a \t tab character."
|
||||||
@@ -876,7 +814,6 @@ notunicode4 = "This string does not have a unicode \\\u0075 escape."`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidStringSimple(t *testing.T) {
|
func TestValidStringSimple(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = "You are not drinking enough whisky."`
|
input := `answer = "You are not drinking enough whisky."`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -889,7 +826,6 @@ func TestValidStringSimple(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidStringWithPound(t *testing.T) {
|
func TestValidStringWithPound(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `pound = "We see no # comments here."
|
input := `pound = "We see no # comments here."
|
||||||
poundcomment = "But there are # some comments here." # Did I # mess you up?`
|
poundcomment = "But there are # some comments here." # Did I # mess you up?`
|
||||||
@@ -904,7 +840,6 @@ poundcomment = "But there are # some comments here." # Did I # mess you up?`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableArrayImplicit(t *testing.T) {
|
func TestValidTableArrayImplicit(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[[albums.songs]]
|
input := `[[albums.songs]]
|
||||||
name = "Glory Days"`
|
name = "Glory Days"`
|
||||||
@@ -919,7 +854,6 @@ name = "Glory Days"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableArrayMany(t *testing.T) {
|
func TestValidTableArrayMany(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[[people]]
|
input := `[[people]]
|
||||||
first_name = "Bruce"
|
first_name = "Bruce"
|
||||||
@@ -952,7 +886,6 @@ last_name = "Seger"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableArrayNest(t *testing.T) {
|
func TestValidTableArrayNest(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[[albums]]
|
input := `[[albums]]
|
||||||
name = "Born to Run"
|
name = "Born to Run"
|
||||||
@@ -993,7 +926,6 @@ name = "Born in the USA"
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableArrayOne(t *testing.T) {
|
func TestValidTableArrayOne(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[[people]]
|
input := `[[people]]
|
||||||
first_name = "Bruce"
|
first_name = "Bruce"
|
||||||
@@ -1010,7 +942,6 @@ last_name = "Springsteen"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableEmpty(t *testing.T) {
|
func TestValidTableEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a]`
|
input := `[a]`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -1020,7 +951,6 @@ func TestValidTableEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableSubEmpty(t *testing.T) {
|
func TestValidTableSubEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `[a]
|
input := `[a]
|
||||||
[a.b]`
|
[a.b]`
|
||||||
@@ -1031,7 +961,6 @@ func TestValidTableSubEmpty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableWhitespace(t *testing.T) {
|
func TestValidTableWhitespace(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `["valid key"]`
|
input := `["valid key"]`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
@@ -1041,7 +970,6 @@ func TestValidTableWhitespace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidTableWithPound(t *testing.T) {
|
func TestValidTableWithPound(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `["key#group"]
|
input := `["key#group"]
|
||||||
answer = 42`
|
answer = 42`
|
||||||
@@ -1054,7 +982,6 @@ answer = 42`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidUnicodeEscape(t *testing.T) {
|
func TestValidUnicodeEscape(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer4 = "\u03B4"
|
input := `answer4 = "\u03B4"
|
||||||
answer8 = "\U000003B4"`
|
answer8 = "\U000003B4"`
|
||||||
@@ -1066,7 +993,6 @@ answer8 = "\U000003B4"`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestValidUnicodeLiteral(t *testing.T) {
|
func TestValidUnicodeLiteral(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
input := `answer = "δ"`
|
input := `answer = "δ"`
|
||||||
jsonRef := `{
|
jsonRef := `{
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeType = reflect.TypeOf(time.Time{})
|
||||||
|
var textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
|
||||||
|
var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
|
||||||
|
var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
|
||||||
|
var sliceInterfaceType = reflect.TypeOf([]interface{}{})
|
||||||
+834
-331
File diff suppressed because it is too large
Load Diff
+259
-37
@@ -14,10 +14,23 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type badReader struct{}
|
||||||
|
|
||||||
|
func (r *badReader) Read([]byte) (int, error) {
|
||||||
|
return 0, fmt.Errorf("testing error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeReaderError(t *testing.T) {
|
||||||
|
r := &badReader{}
|
||||||
|
|
||||||
|
dec := toml.NewDecoder(r)
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
err := dec.Decode(&m)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
// nolint:funlen
|
// nolint:funlen
|
||||||
func TestUnmarshal_Integers(t *testing.T) {
|
func TestUnmarshal_Integers(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
@@ -88,8 +101,6 @@ func TestUnmarshal_Integers(t *testing.T) {
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
doc := doc{}
|
doc := doc{}
|
||||||
err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
|
err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
|
||||||
if e.err {
|
if e.err {
|
||||||
@@ -104,8 +115,6 @@ func TestUnmarshal_Integers(t *testing.T) {
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestUnmarshal_Floats(t *testing.T) {
|
func TestUnmarshal_Floats(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
@@ -197,8 +206,6 @@ func TestUnmarshal_Floats(t *testing.T) {
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
doc := doc{}
|
doc := doc{}
|
||||||
err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
|
err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -213,8 +220,6 @@ func TestUnmarshal_Floats(t *testing.T) {
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestUnmarshal(t *testing.T) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
target interface{}
|
target interface{}
|
||||||
expected interface{}
|
expected interface{}
|
||||||
@@ -814,6 +819,240 @@ B = "data"`,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "array table into interface in struct",
|
||||||
|
input: `[[foo]]
|
||||||
|
bar = "hello"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
Foo interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{
|
||||||
|
Foo: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"bar": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "array table into interface in struct already initialized with right type",
|
||||||
|
input: `[[foo]]
|
||||||
|
bar = "hello"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
Foo interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{
|
||||||
|
Foo: []interface{}{},
|
||||||
|
},
|
||||||
|
expected: &doc{
|
||||||
|
Foo: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"bar": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "array table into interface in struct already initialized with wrong type",
|
||||||
|
input: `[[foo]]
|
||||||
|
bar = "hello"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
Foo interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{
|
||||||
|
Foo: []string{},
|
||||||
|
},
|
||||||
|
expected: &doc{
|
||||||
|
Foo: []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"bar": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "array table into nil ptr",
|
||||||
|
input: `[[foo]]
|
||||||
|
bar = "hello"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
Foo *[]interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{
|
||||||
|
Foo: &[]interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"bar": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "array table into nil ptr of invalid type",
|
||||||
|
input: `[[foo]]
|
||||||
|
bar = "hello"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
Foo *string
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
err: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "array table with intermediate ptr",
|
||||||
|
input: `[[foo.bar]]
|
||||||
|
bar = "hello"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
Foo *map[string]interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{
|
||||||
|
Foo: &map[string]interface{}{
|
||||||
|
"bar": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"bar": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal array into interface that contains a slice",
|
||||||
|
input: `a = [1,2,3]`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{
|
||||||
|
A: []string{},
|
||||||
|
},
|
||||||
|
expected: &doc{
|
||||||
|
A: []interface{}{
|
||||||
|
int64(1),
|
||||||
|
int64(2),
|
||||||
|
int64(3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal array into interface that contains a []interface{}",
|
||||||
|
input: `a = [1,2,3]`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{
|
||||||
|
A: []interface{}{},
|
||||||
|
},
|
||||||
|
expected: &doc{
|
||||||
|
A: []interface{}{
|
||||||
|
int64(1),
|
||||||
|
int64(2),
|
||||||
|
int64(3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal key into map with existing value",
|
||||||
|
input: `a = "new"`,
|
||||||
|
gen: func() test {
|
||||||
|
return test{
|
||||||
|
target: &map[string]interface{}{"a": "old"},
|
||||||
|
expected: &map[string]interface{}{"a": "new"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal key into map with existing value",
|
||||||
|
input: `a.b = "new"`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{
|
||||||
|
A: map[string]interface{}{
|
||||||
|
"b": "new",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal array into struct field with existing array",
|
||||||
|
input: `a = [1,2]`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A []int
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{
|
||||||
|
A: []int{1, 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal inline table into map",
|
||||||
|
input: `a = {b="hello"}`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A map[string]interface{}
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
expected: &doc{
|
||||||
|
A: map[string]interface{}{
|
||||||
|
"b": "hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unmarshal inline table into map of incorrect type",
|
||||||
|
input: `a = {b="hello"}`,
|
||||||
|
gen: func() test {
|
||||||
|
type doc struct {
|
||||||
|
A map[string]int
|
||||||
|
}
|
||||||
|
return test{
|
||||||
|
target: &doc{},
|
||||||
|
err: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "slice pointer in slice pointer",
|
desc: "slice pointer in slice pointer",
|
||||||
input: `A = ["Hello"]`,
|
input: `A = ["Hello"]`,
|
||||||
@@ -1155,8 +1394,6 @@ B = "data"`,
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
if e.skip {
|
if e.skip {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
}
|
}
|
||||||
@@ -1241,6 +1478,16 @@ func TestUnmarshalOverflows(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInvalidTarget(t *testing.T) {
|
||||||
|
x := "foo"
|
||||||
|
err := toml.Unmarshal([]byte{}, x)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
var m *map[string]interface{}
|
||||||
|
err = toml.Unmarshal([]byte{}, m)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnmarshalFloat32(t *testing.T) {
|
func TestUnmarshalFloat32(t *testing.T) {
|
||||||
t.Run("fits", func(t *testing.T) {
|
t.Run("fits", func(t *testing.T) {
|
||||||
doc := "A = 1.2"
|
doc := "A = 1.2"
|
||||||
@@ -1277,8 +1524,6 @@ type Config484 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue484(t *testing.T) {
|
func TestIssue484(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
raw := []byte(`integers = ["1","2","3","100"]`)
|
raw := []byte(`integers = ["1","2","3","100"]`)
|
||||||
|
|
||||||
var cfg Config484
|
var cfg Config484
|
||||||
@@ -1299,8 +1544,6 @@ func (m Map458) A(s string) Slice458 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue458(t *testing.T) {
|
func TestIssue458(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
s := []byte(`[[package]]
|
s := []byte(`[[package]]
|
||||||
dependencies = ["regex"]
|
dependencies = ["regex"]
|
||||||
name = "decode"
|
name = "decode"
|
||||||
@@ -1320,8 +1563,6 @@ version = "0.1.0"`)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue252(t *testing.T) {
|
func TestIssue252(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Val1 string `toml:"val1"`
|
Val1 string `toml:"val1"`
|
||||||
Val2 string `toml:"val2"`
|
Val2 string `toml:"val2"`
|
||||||
@@ -1342,8 +1583,6 @@ val1 = "test1"
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue494(t *testing.T) {
|
func TestIssue494(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
data := `
|
data := `
|
||||||
foo = 2021-04-08
|
foo = 2021-04-08
|
||||||
bar = 2021-04-08
|
bar = 2021-04-08
|
||||||
@@ -1359,8 +1598,6 @@ bar = 2021-04-08
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue507(t *testing.T) {
|
func TestIssue507(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
data := []byte{'0', '=', '\n', '0', 'a', 'm', 'e'}
|
data := []byte{'0', '=', '\n', '0', 'a', 'm', 'e'}
|
||||||
m := map[string]interface{}{}
|
m := map[string]interface{}{}
|
||||||
err := toml.Unmarshal(data, &m)
|
err := toml.Unmarshal(data, &m)
|
||||||
@@ -1369,8 +1606,6 @@ func TestIssue507(t *testing.T) {
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestUnmarshalDecodeErrors(t *testing.T) {
|
func TestUnmarshalDecodeErrors(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
data string
|
data string
|
||||||
@@ -1603,8 +1838,6 @@ world'`,
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
m := map[string]interface{}{}
|
m := map[string]interface{}{}
|
||||||
err := toml.Unmarshal([]byte(e.data), &m)
|
err := toml.Unmarshal([]byte(e.data), &m)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -1624,8 +1857,6 @@ world'`,
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestLocalDateTime(t *testing.T) {
|
func TestLocalDateTime(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
@@ -1675,7 +1906,6 @@ func TestLocalDateTime(t *testing.T) {
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
t.Log("input:", e.input)
|
t.Log("input:", e.input)
|
||||||
doc := `a = ` + e.input
|
doc := `a = ` + e.input
|
||||||
m := map[string]toml.LocalDateTime{}
|
m := map[string]toml.LocalDateTime{}
|
||||||
@@ -1691,8 +1921,6 @@ func TestLocalDateTime(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue287(t *testing.T) {
|
func TestIssue287(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
b := `y=[[{}]]`
|
b := `y=[[{}]]`
|
||||||
v := map[string]interface{}{}
|
v := map[string]interface{}{}
|
||||||
err := toml.Unmarshal([]byte(b), &v)
|
err := toml.Unmarshal([]byte(b), &v)
|
||||||
@@ -1709,8 +1937,6 @@ func TestIssue287(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue508(t *testing.T) {
|
func TestIssue508(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type head struct {
|
type head struct {
|
||||||
Title string `toml:"title"`
|
Title string `toml:"title"`
|
||||||
}
|
}
|
||||||
@@ -1729,8 +1955,6 @@ func TestIssue508(t *testing.T) {
|
|||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestDecoderStrict(t *testing.T) {
|
func TestDecoderStrict(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
@@ -1801,8 +2025,6 @@ bar = 42
|
|||||||
for _, e := range examples {
|
for _, e := range examples {
|
||||||
e := e
|
e := e
|
||||||
t.Run(e.desc, func(t *testing.T) {
|
t.Run(e.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
t.Run("strict", func(t *testing.T) {
|
t.Run("strict", func(t *testing.T) {
|
||||||
r := strings.NewReader(e.input)
|
r := strings.NewReader(e.input)
|
||||||
d := toml.NewDecoder(r)
|
d := toml.NewDecoder(r)
|
||||||
|
|||||||
Reference in New Issue
Block a user