Compare commits
18 Commits
v2.0.0-beta.7
...
v2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 216628222f | |||
| 322e0b15d2 | |||
| 85bfc0ed51 | |||
| 295a720dfb | |||
| 0a422e3dbd | |||
| 627dade0c7 | |||
| b2e0231cc9 | |||
| ba95863cd3 | |||
| db679df765 | |||
| c5ca2c682b | |||
| ed80712cb4 | |||
| b24772942d | |||
| 9501a05ed7 | |||
| 171a592663 | |||
| 5aaf5ef13b | |||
| adacebd8c7 | |||
| 8bbb673431 | |||
| 2377ac4bc0 |
@@ -1,3 +1,4 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
benchmark/benchmark.toml text eol=lf
|
benchmark/benchmark.toml text eol=lf
|
||||||
|
testdata/** text eol=lf
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -64,4 +64,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
name: report
|
name: report
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
@@ -24,13 +24,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: 1.18
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v2
|
uses: goreleaser/goreleaser-action@v3
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
version: latest
|
version: latest
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
name: ${{ matrix.go }}/${{ matrix.os }}
|
name: ${{ matrix.go }}/${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup go ${{ matrix.go }}
|
- name: Setup go ${{ matrix.go }}
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ builds:
|
|||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
targets:
|
targets:
|
||||||
- linux_amd64
|
- linux_amd64
|
||||||
|
- linux_arm64
|
||||||
|
- linux_arm
|
||||||
- windows_amd64
|
- windows_amd64
|
||||||
|
- windows_arm64
|
||||||
|
- windows_arm
|
||||||
- darwin_amd64
|
- darwin_amd64
|
||||||
- darwin_arm64
|
- darwin_arm64
|
||||||
- id: tomljson
|
- id: tomljson
|
||||||
@@ -31,7 +35,11 @@ builds:
|
|||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
targets:
|
targets:
|
||||||
- linux_amd64
|
- linux_amd64
|
||||||
|
- linux_arm64
|
||||||
|
- linux_arm
|
||||||
- windows_amd64
|
- windows_amd64
|
||||||
|
- windows_arm64
|
||||||
|
- windows_arm
|
||||||
- darwin_amd64
|
- darwin_amd64
|
||||||
- darwin_arm64
|
- darwin_arm64
|
||||||
- id: jsontoml
|
- id: jsontoml
|
||||||
@@ -46,7 +54,11 @@ builds:
|
|||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
targets:
|
targets:
|
||||||
- linux_amd64
|
- linux_amd64
|
||||||
|
- linux_arm64
|
||||||
|
- linux_arm
|
||||||
- windows_amd64
|
- windows_amd64
|
||||||
|
- windows_arm64
|
||||||
|
- windows_arm
|
||||||
- darwin_amd64
|
- darwin_amd64
|
||||||
- darwin_arm64
|
- darwin_arm64
|
||||||
universal_binaries:
|
universal_binaries:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 - 2021 Thomas Pelletier, Eric Anderton
|
Copyright (c) 2013 - 2022 Thomas Pelletier, Eric Anderton
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -4,17 +4,6 @@ Go library for the [TOML](https://toml.io/en/) format.
|
|||||||
|
|
||||||
This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0).
|
This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0).
|
||||||
|
|
||||||
## Development status
|
|
||||||
|
|
||||||
This is the upcoming major version of go-toml. It is currently in active
|
|
||||||
development. As of release v2.0.0-beta.1, the library has reached feature parity
|
|
||||||
with v1, and fixes a lot known bugs and performance issues along the way.
|
|
||||||
|
|
||||||
If you do not need the advanced document editing features of v1, you are
|
|
||||||
encouraged to try out this version.
|
|
||||||
|
|
||||||
[👉 Roadmap for v2](https://github.com/pelletier/go-toml/discussions/506)
|
|
||||||
|
|
||||||
[🐞 Bug Reports](https://github.com/pelletier/go-toml/issues)
|
[🐞 Bug Reports](https://github.com/pelletier/go-toml/issues)
|
||||||
|
|
||||||
[💬 Anything else](https://github.com/pelletier/go-toml/discussions)
|
[💬 Anything else](https://github.com/pelletier/go-toml/discussions)
|
||||||
@@ -49,7 +38,7 @@ operations should not be shockingly slow. See [benchmarks](#benchmarks).
|
|||||||
### Strict mode
|
### Strict mode
|
||||||
|
|
||||||
`Decoder` can be set to "strict mode", which makes it error when some parts of
|
`Decoder` can be set to "strict mode", which makes it error when some parts of
|
||||||
the TOML document was not prevent in the target structure. This is a great way
|
the TOML document was not present in the target structure. This is a great way
|
||||||
to check for typos. [See example in the documentation][strict].
|
to check for typos. [See example in the documentation][strict].
|
||||||
|
|
||||||
[strict]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Decoder.DisallowUnknownFields
|
[strict]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Decoder.DisallowUnknownFields
|
||||||
@@ -161,11 +150,11 @@ Execution time speedup compared to other Go TOML libraries:
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>1.9x</td></tr>
|
<tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>1.9x</td></tr>
|
||||||
<tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>1.9x</td></tr>
|
<tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>1.8x</td></tr>
|
||||||
<tr><td>Marshal/ReferenceFile/struct-2</td><td>2.4x</td><td>2.6x</td></tr>
|
<tr><td>Marshal/ReferenceFile/struct-2</td><td>2.2x</td><td>2.5x</td></tr>
|
||||||
<tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.5x</td></tr>
|
<tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.9x</td></tr>
|
||||||
<tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.7x</td><td>2.6x</td></tr>
|
<tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.6x</td><td>2.9x</td></tr>
|
||||||
<tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.8x</td><td>5.1x</td></tr>
|
<tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.4x</td><td>5.3x</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<details><summary>See more</summary>
|
<details><summary>See more</summary>
|
||||||
@@ -178,17 +167,17 @@ provided for completeness.</p>
|
|||||||
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
|
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>Marshal/SimpleDocument/map-2</td><td>1.7x</td><td>2.1x</td></tr>
|
<tr><td>Marshal/SimpleDocument/map-2</td><td>1.8x</td><td>2.9x</td></tr>
|
||||||
<tr><td>Marshal/SimpleDocument/struct-2</td><td>2.5x</td><td>2.8x</td></tr>
|
<tr><td>Marshal/SimpleDocument/struct-2</td><td>2.7x</td><td>4.2x</td></tr>
|
||||||
<tr><td>Unmarshal/SimpleDocument/map-2</td><td>4.1x</td><td>3.1x</td></tr>
|
<tr><td>Unmarshal/SimpleDocument/map-2</td><td>4.5x</td><td>3.1x</td></tr>
|
||||||
<tr><td>Unmarshal/SimpleDocument/struct-2</td><td>6.4x</td><td>4.3x</td></tr>
|
<tr><td>Unmarshal/SimpleDocument/struct-2</td><td>6.2x</td><td>3.9x</td></tr>
|
||||||
<tr><td>UnmarshalDataset/example-2</td><td>3.4x</td><td>3.2x</td></tr>
|
<tr><td>UnmarshalDataset/example-2</td><td>3.1x</td><td>3.5x</td></tr>
|
||||||
<tr><td>UnmarshalDataset/code-2</td><td>2.2x</td><td>2.5x</td></tr>
|
<tr><td>UnmarshalDataset/code-2</td><td>2.3x</td><td>3.1x</td></tr>
|
||||||
<tr><td>UnmarshalDataset/twitter-2</td><td>2.8x</td><td>2.7x</td></tr>
|
<tr><td>UnmarshalDataset/twitter-2</td><td>2.5x</td><td>2.6x</td></tr>
|
||||||
<tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.2x</td><td>2.0x</td></tr>
|
<tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.1x</td><td>2.2x</td></tr>
|
||||||
<tr><td>UnmarshalDataset/canada-2</td><td>1.8x</td><td>1.4x</td></tr>
|
<tr><td>UnmarshalDataset/canada-2</td><td>1.6x</td><td>1.3x</td></tr>
|
||||||
<tr><td>UnmarshalDataset/config-2</td><td>4.4x</td><td>2.9x</td></tr>
|
<tr><td>UnmarshalDataset/config-2</td><td>4.3x</td><td>3.2x</td></tr>
|
||||||
<tr><td>[Geo mean]</td><td>2.8x</td><td>2.6x</td></tr>
|
<tr><td>[Geo mean]</td><td>2.7x</td><td>2.8x</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>
|
<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>
|
||||||
@@ -551,6 +540,13 @@ complete solutions exist out there.
|
|||||||
[query]: https://github.com/pelletier/go-toml/tree/f99d6bbca119636aeafcf351ee52b3d202782627/query
|
[query]: https://github.com/pelletier/go-toml/tree/f99d6bbca119636aeafcf351ee52b3d202782627/query
|
||||||
[dasel]: https://github.com/TomWright/dasel
|
[dasel]: https://github.com/TomWright/dasel
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Go-toml follows [Semantic Versioning](http://semver.org/). The supported version
|
||||||
|
of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of
|
||||||
|
this document. The last two major versions of Go are supported
|
||||||
|
(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The MIT License (MIT). Read [LICENSE](LICENSE).
|
The MIT License (MIT). Read [LICENSE](LICENSE).
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ cover() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
pushd "$dir"
|
pushd "$dir"
|
||||||
go test -covermode=atomic -coverprofile=coverage.out ./...
|
go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out.tmp ./...
|
||||||
|
cat coverage.out.tmp | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out
|
||||||
go tool cover -func=coverage.out
|
go tool cover -func=coverage.out
|
||||||
popd
|
popd
|
||||||
|
|
||||||
@@ -103,8 +104,8 @@ coverage() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
target_pct="$(cat ${target_out} |sed -E 's/.*total.*\t([0-9.]+)%/\1/;t;d')"
|
target_pct="$(tail -n2 ${target_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%.*/\1/')"
|
||||||
head_pct="$(cat ${head_out} |sed -E 's/.*total.*\t([0-9.]+)%/\1/;t;d')"
|
head_pct="$(tail -n2 ${head_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%/\1/')"
|
||||||
echo "Results: ${target} ${target_pct}% HEAD ${head_pct}%"
|
echo "Results: ${target} ${target_pct}% HEAD ${head_pct}%"
|
||||||
|
|
||||||
delta_pct=$(echo "$head_pct - $target_pct" | bc -l)
|
delta_pct=$(echo "$head_pct - $target_pct" | bc -l)
|
||||||
@@ -112,6 +113,13 @@ coverage() {
|
|||||||
|
|
||||||
if [[ $delta_pct = \-* ]]; then
|
if [[ $delta_pct = \-* ]]; then
|
||||||
echo "Regression!";
|
echo "Regression!";
|
||||||
|
|
||||||
|
target_diff="${output_dir}/target.diff.txt"
|
||||||
|
head_diff="${output_dir}/head.diff.txt"
|
||||||
|
cat "${target_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${target_diff}"
|
||||||
|
cat "${head_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${head_diff}"
|
||||||
|
|
||||||
|
diff --side-by-side --suppress-common-lines "${target_diff}" "${head_diff}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -130,7 +130,11 @@ func parseDateTime(b []byte) (time.Time, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
seconds := direction * (hours*3600 + minutes*60)
|
seconds := direction * (hours*3600 + minutes*60)
|
||||||
|
if seconds == 0 {
|
||||||
|
zone = time.UTC
|
||||||
|
} else {
|
||||||
zone = time.FixedZone("", seconds)
|
zone = time.FixedZone("", seconds)
|
||||||
|
}
|
||||||
b = b[dateTimeByteLen:]
|
b = b[dateTimeByteLen:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package toml_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pelletier/go-toml/v2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzUnmarshal(f *testing.F) {
|
||||||
|
file, err := ioutil.ReadFile("benchmark/benchmark.toml")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
f.Add(file)
|
||||||
|
|
||||||
|
f.Fuzz(func(t *testing.T, b []byte) {
|
||||||
|
if strings.Contains(string(b), "nan") {
|
||||||
|
// Current limitation of testify.
|
||||||
|
// https://github.com/stretchr/testify/issues/624
|
||||||
|
t.Skip("can't compare NaNs")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("INITIAL DOCUMENT ===========================")
|
||||||
|
t.Log(string(b))
|
||||||
|
|
||||||
|
var v interface{}
|
||||||
|
err := toml.Unmarshal(b, &v)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("DECODED VALUE ===========================")
|
||||||
|
t.Logf("%#+v", v)
|
||||||
|
|
||||||
|
encoded, err := toml.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot marshal unmarshaled document: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("ENCODED DOCUMENT ===========================")
|
||||||
|
t.Log(string(encoded))
|
||||||
|
|
||||||
|
var v2 interface{}
|
||||||
|
err = toml.Unmarshal(encoded, &v2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed round trip: %s", err)
|
||||||
|
}
|
||||||
|
require.Equal(t, v, v2)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -2,4 +2,4 @@ module github.com/pelletier/go-toml/v2
|
|||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require github.com/stretchr/testify v1.7.1
|
require github.com/stretchr/testify v1.7.2
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ func TestEntrySize(t *testing.T) {
|
|||||||
// Validate no regression on the size of entry{}. This is a critical bit for
|
// Validate no regression on the size of entry{}. This is a critical bit for
|
||||||
// performance of unmarshaling documents. Should only be increased with care
|
// performance of unmarshaling documents. Should only be increased with care
|
||||||
// and a very good reason.
|
// and a very good reason.
|
||||||
require.LessOrEqual(t, 48, int(unsafe.Sizeof(entry{})))
|
maxExpectedEntrySize := 48
|
||||||
|
require.LessOrEqual(t, int(unsafe.Sizeof(entry{})), maxExpectedEntrySize)
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-50
@@ -107,6 +107,13 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
|
|||||||
// a newline character or a single quote. In that case they are emitted as
|
// a newline character or a single quote. In that case they are emitted as
|
||||||
// quoted strings.
|
// quoted strings.
|
||||||
//
|
//
|
||||||
|
// Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so
|
||||||
|
// results in an error. This rule exists because the TOML specification only
|
||||||
|
// requires parsers to support at least the 64 bits integer range. Allowing
|
||||||
|
// larger numbers would create non-standard TOML documents, which may not be
|
||||||
|
// readable (at best) by other implementations. To encode such numbers, a
|
||||||
|
// solution is a custom type that implements encoding.TextMarshaler.
|
||||||
|
//
|
||||||
// When encoding structs, fields are encoded in order of definition, with their
|
// When encoding structs, fields are encoded in order of definition, with their
|
||||||
// exact name.
|
// exact name.
|
||||||
//
|
//
|
||||||
@@ -128,7 +135,8 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
|
|||||||
//
|
//
|
||||||
// In addition to the "toml" tag struct tag, a "comment" tag can be used to emit
|
// In addition to the "toml" tag struct tag, a "comment" tag can be used to emit
|
||||||
// a TOML comment before the value being annotated. Comments are ignored inside
|
// a TOML comment before the value being annotated. Comments are ignored inside
|
||||||
// inline tables.
|
// inline tables. For array tables, the comment is only present before the first
|
||||||
|
// element of the array.
|
||||||
func (enc *Encoder) Encode(v interface{}) error {
|
func (enc *Encoder) Encode(v interface{}) error {
|
||||||
var (
|
var (
|
||||||
b []byte
|
b []byte
|
||||||
@@ -208,11 +216,20 @@ func (ctx *encoderCtx) isRoot() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
|
func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
|
||||||
if !v.IsZero() {
|
i := v.Interface()
|
||||||
i, ok := v.Interface().(time.Time)
|
|
||||||
if ok {
|
switch x := i.(type) {
|
||||||
return i.AppendFormat(b, time.RFC3339), nil
|
case time.Time:
|
||||||
|
if x.Nanosecond() > 0 {
|
||||||
|
return x.AppendFormat(b, time.RFC3339Nano), nil
|
||||||
}
|
}
|
||||||
|
return x.AppendFormat(b, time.RFC3339), nil
|
||||||
|
case LocalTime:
|
||||||
|
return append(b, x.String()...), nil
|
||||||
|
case LocalDate:
|
||||||
|
return append(b, x.String()...), nil
|
||||||
|
case LocalDateTime:
|
||||||
|
return append(b, x.String()...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hasTextMarshaler := v.Type().Implements(textMarshalerType)
|
hasTextMarshaler := v.Type().Implements(textMarshalerType)
|
||||||
@@ -260,16 +277,31 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
|
|||||||
case reflect.String:
|
case reflect.String:
|
||||||
b = enc.encodeString(b, v.String(), ctx.options)
|
b = enc.encodeString(b, v.String(), ctx.options)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
if math.Trunc(v.Float()) == v.Float() {
|
f := v.Float()
|
||||||
b = strconv.AppendFloat(b, v.Float(), 'f', 1, 32)
|
|
||||||
|
if math.IsNaN(f) {
|
||||||
|
b = append(b, "nan"...)
|
||||||
|
} else if f > math.MaxFloat32 {
|
||||||
|
b = append(b, "inf"...)
|
||||||
|
} else if f < -math.MaxFloat32 {
|
||||||
|
b = append(b, "-inf"...)
|
||||||
|
} else if math.Trunc(f) == f {
|
||||||
|
b = strconv.AppendFloat(b, f, 'f', 1, 32)
|
||||||
} else {
|
} else {
|
||||||
b = strconv.AppendFloat(b, v.Float(), 'f', -1, 32)
|
b = strconv.AppendFloat(b, f, 'f', -1, 32)
|
||||||
}
|
}
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
if math.Trunc(v.Float()) == v.Float() {
|
f := v.Float()
|
||||||
b = strconv.AppendFloat(b, v.Float(), 'f', 1, 64)
|
if math.IsNaN(f) {
|
||||||
|
b = append(b, "nan"...)
|
||||||
|
} else if f > math.MaxFloat64 {
|
||||||
|
b = append(b, "inf"...)
|
||||||
|
} else if f < -math.MaxFloat64 {
|
||||||
|
b = append(b, "-inf"...)
|
||||||
|
} else if math.Trunc(f) == f {
|
||||||
|
b = strconv.AppendFloat(b, f, 'f', 1, 64)
|
||||||
} else {
|
} else {
|
||||||
b = strconv.AppendFloat(b, v.Float(), 'f', -1, 64)
|
b = strconv.AppendFloat(b, f, 'f', -1, 64)
|
||||||
}
|
}
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
if v.Bool() {
|
if v.Bool() {
|
||||||
@@ -278,7 +310,11 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
|
|||||||
b = append(b, "false"...)
|
b = append(b, "false"...)
|
||||||
}
|
}
|
||||||
case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
|
case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
|
||||||
b = strconv.AppendUint(b, v.Uint(), 10)
|
x := v.Uint()
|
||||||
|
if x > uint64(math.MaxInt64) {
|
||||||
|
return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64))
|
||||||
|
}
|
||||||
|
b = strconv.AppendUint(b, x, 10)
|
||||||
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
|
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
|
||||||
b = strconv.AppendInt(b, v.Int(), 10)
|
b = strconv.AppendInt(b, v.Int(), 10)
|
||||||
default:
|
default:
|
||||||
@@ -300,10 +336,6 @@ func isNil(v reflect.Value) bool {
|
|||||||
func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
|
func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if !ctx.hasKey {
|
|
||||||
panic("caller of encodeKv should have set the key in the context")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.options.omitempty || options.omitempty) && isEmptyValue(v) {
|
if (ctx.options.omitempty || options.omitempty) && isEmptyValue(v) {
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
@@ -313,12 +345,7 @@ func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v r
|
|||||||
}
|
}
|
||||||
|
|
||||||
b = enc.indent(ctx.indent, b)
|
b = enc.indent(ctx.indent, b)
|
||||||
|
b = enc.encodeKey(b, ctx.key)
|
||||||
b, err = enc.encodeKey(b, ctx.key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b = append(b, " = "...)
|
b = append(b, " = "...)
|
||||||
|
|
||||||
// create a copy of the context because the value of a KV shouldn't
|
// create a copy of the context because the value of a KV shouldn't
|
||||||
@@ -365,7 +392,13 @@ func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func needsQuoting(v string) bool {
|
func needsQuoting(v string) bool {
|
||||||
return strings.ContainsAny(v, "'\b\f\n\r\t")
|
// TODO: vectorize
|
||||||
|
for _, b := range []byte(v) {
|
||||||
|
if b == '\'' || b == '\r' || b == '\n' || invalidAscii(b) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// caller should have checked that the string does not contain new lines or ' .
|
// caller should have checked that the string does not contain new lines or ' .
|
||||||
@@ -437,7 +470,7 @@ func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byt
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// called should have checked that the string is in A-Z / a-z / 0-9 / - / _ .
|
// caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ .
|
||||||
func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte {
|
func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte {
|
||||||
return append(b, v...)
|
return append(b, v...)
|
||||||
}
|
}
|
||||||
@@ -453,20 +486,11 @@ func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error)
|
|||||||
|
|
||||||
b = append(b, '[')
|
b = append(b, '[')
|
||||||
|
|
||||||
var err error
|
b = enc.encodeKey(b, ctx.parentKey[0])
|
||||||
|
|
||||||
b, err = enc.encodeKey(b, ctx.parentKey[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, k := range ctx.parentKey[1:] {
|
for _, k := range ctx.parentKey[1:] {
|
||||||
b = append(b, '.')
|
b = append(b, '.')
|
||||||
|
b = enc.encodeKey(b, k)
|
||||||
b, err = enc.encodeKey(b, k)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b = append(b, "]\n"...)
|
b = append(b, "]\n"...)
|
||||||
@@ -475,19 +499,19 @@ func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//nolint:cyclop
|
//nolint:cyclop
|
||||||
func (enc *Encoder) encodeKey(b []byte, k string) ([]byte, error) {
|
func (enc *Encoder) encodeKey(b []byte, k string) []byte {
|
||||||
needsQuotation := false
|
needsQuotation := false
|
||||||
cannotUseLiteral := false
|
cannotUseLiteral := false
|
||||||
|
|
||||||
|
if len(k) == 0 {
|
||||||
|
return append(b, "''"...)
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range k {
|
for _, c := range k {
|
||||||
if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
|
if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if c == '\n' {
|
|
||||||
return nil, fmt.Errorf("toml: new line characters in keys are not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == literalQuote {
|
if c == literalQuote {
|
||||||
cannotUseLiteral = true
|
cannotUseLiteral = true
|
||||||
}
|
}
|
||||||
@@ -495,13 +519,17 @@ func (enc *Encoder) encodeKey(b []byte, k string) ([]byte, error) {
|
|||||||
needsQuotation = true
|
needsQuotation = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if needsQuotation && needsQuoting(k) {
|
||||||
|
cannotUseLiteral = true
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case cannotUseLiteral:
|
case cannotUseLiteral:
|
||||||
return enc.encodeQuotedString(false, b, k), nil
|
return enc.encodeQuotedString(false, b, k)
|
||||||
case needsQuotation:
|
case needsQuotation:
|
||||||
return enc.encodeLiteralString(b, k), nil
|
return enc.encodeLiteralString(b, k)
|
||||||
default:
|
default:
|
||||||
return enc.encodeUnquotedKey(b, k), nil
|
return enc.encodeUnquotedKey(b, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,7 +628,9 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
|
|||||||
|
|
||||||
if k == "" {
|
if k == "" {
|
||||||
if fieldType.Anonymous {
|
if fieldType.Anonymous {
|
||||||
|
if fieldType.Type.Kind() == reflect.Struct {
|
||||||
walkStruct(ctx, t, f)
|
walkStruct(ctx, t, f)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
k = fieldType.Name
|
k = fieldType.Name
|
||||||
@@ -634,10 +664,19 @@ func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]b
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte {
|
func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte {
|
||||||
if comment != "" {
|
for len(comment) > 0 {
|
||||||
|
var line string
|
||||||
|
idx := strings.IndexByte(comment, '\n')
|
||||||
|
if idx >= 0 {
|
||||||
|
line = comment[:idx]
|
||||||
|
comment = comment[idx+1:]
|
||||||
|
} else {
|
||||||
|
line = comment
|
||||||
|
comment = ""
|
||||||
|
}
|
||||||
b = enc.indent(indent, b)
|
b = enc.indent(indent, b)
|
||||||
b = append(b, "# "...)
|
b = append(b, "# "...)
|
||||||
b = append(b, comment...)
|
b = append(b, line...)
|
||||||
b = append(b, '\n')
|
b = append(b, '\n')
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
@@ -801,6 +840,9 @@ func willConvertToTable(ctx encoderCtx, v reflect.Value) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
|
func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
|
||||||
|
if ctx.insideKv {
|
||||||
|
return false
|
||||||
|
}
|
||||||
t := v.Type()
|
t := v.Type()
|
||||||
|
|
||||||
if t.Kind() == reflect.Interface {
|
if t.Kind() == reflect.Interface {
|
||||||
@@ -846,7 +888,6 @@ func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]by
|
|||||||
func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
|
func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) {
|
||||||
ctx.shiftKey()
|
ctx.shiftKey()
|
||||||
|
|
||||||
var err error
|
|
||||||
scratch := make([]byte, 0, 64)
|
scratch := make([]byte, 0, 64)
|
||||||
scratch = append(scratch, "[["...)
|
scratch = append(scratch, "[["...)
|
||||||
|
|
||||||
@@ -855,18 +896,18 @@ func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.
|
|||||||
scratch = append(scratch, '.')
|
scratch = append(scratch, '.')
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch, err = enc.encodeKey(scratch, k)
|
scratch = enc.encodeKey(scratch, k)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch = append(scratch, "]]\n"...)
|
scratch = append(scratch, "]]\n"...)
|
||||||
ctx.skipTableHeader = true
|
ctx.skipTableHeader = true
|
||||||
|
|
||||||
|
b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
|
||||||
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
for i := 0; i < v.Len(); i++ {
|
||||||
b = append(b, scratch...)
|
b = append(b, scratch...)
|
||||||
|
|
||||||
|
var err error
|
||||||
b, err = enc.encode(b, ctx, v.Index(i))
|
b, err = enc.encode(b, ctx, v.Index(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+110
-6
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -45,7 +46,7 @@ func TestMarshal(t *testing.T) {
|
|||||||
v: map[string]string{
|
v: map[string]string{
|
||||||
"hel\nlo": "world",
|
"hel\nlo": "world",
|
||||||
},
|
},
|
||||||
err: true,
|
expected: `"hel\nlo" = 'world'`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: `map with " in key`,
|
desc: `map with " in key`,
|
||||||
@@ -380,7 +381,8 @@ hello = 'world'`,
|
|||||||
v: map[string][]map[string]string{
|
v: map[string][]map[string]string{
|
||||||
"a\n": {{"hello": "world"}},
|
"a\n": {{"hello": "world"}},
|
||||||
},
|
},
|
||||||
err: true,
|
expected: `[["a\n"]]
|
||||||
|
hello = 'world'`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "newline in map in slice",
|
desc: "newline in map in slice",
|
||||||
@@ -440,7 +442,7 @@ hello = 'world'`,
|
|||||||
v: map[string]interface{}{
|
v: map[string]interface{}{
|
||||||
"hello\nworld": 42,
|
"hello\nworld": 42,
|
||||||
},
|
},
|
||||||
err: true,
|
expected: `"hello\nworld" = 42`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "new line in parent of nested table key",
|
desc: "new line in parent of nested table key",
|
||||||
@@ -449,7 +451,8 @@ hello = 'world'`,
|
|||||||
"inner": 42,
|
"inner": 42,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: true,
|
expected: `["hello\nworld"]
|
||||||
|
inner = 42`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "new line in nested table key",
|
desc: "new line in nested table key",
|
||||||
@@ -460,7 +463,9 @@ hello = 'world'`,
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: true,
|
expected: `[parent]
|
||||||
|
[parent."in\ner"]
|
||||||
|
foo = 42`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "invalid map key",
|
desc: "invalid map key",
|
||||||
@@ -483,7 +488,16 @@ hello = 'world'`,
|
|||||||
}{
|
}{
|
||||||
T: time.Time{},
|
T: time.Time{},
|
||||||
},
|
},
|
||||||
expected: `T = '0001-01-01T00:00:00Z'`,
|
expected: `T = 0001-01-01T00:00:00Z`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "time nano",
|
||||||
|
v: struct {
|
||||||
|
T time.Time
|
||||||
|
}{
|
||||||
|
T: time.Date(1979, time.May, 27, 0, 32, 0, 999999000, time.UTC),
|
||||||
|
},
|
||||||
|
expected: `T = 1979-05-27T00:32:00.999999Z`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "bool",
|
desc: "bool",
|
||||||
@@ -656,6 +670,33 @@ func equalStringsIgnoreNewlines(t *testing.T, expected string, actual string) {
|
|||||||
assert.Equal(t, strings.Trim(expected, cutset), strings.Trim(actual, cutset))
|
assert.Equal(t, strings.Trim(expected, cutset), strings.Trim(actual, cutset))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshalFloats(t *testing.T) {
|
||||||
|
v := map[string]float32{
|
||||||
|
"nan": float32(math.NaN()),
|
||||||
|
"+inf": float32(math.Inf(1)),
|
||||||
|
"-inf": float32(math.Inf(-1)),
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `'+inf' = inf
|
||||||
|
-inf = -inf
|
||||||
|
nan = nan
|
||||||
|
`
|
||||||
|
|
||||||
|
actual, err := toml.Marshal(v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, string(actual))
|
||||||
|
|
||||||
|
v64 := map[string]float64{
|
||||||
|
"nan": math.NaN(),
|
||||||
|
"+inf": math.Inf(1),
|
||||||
|
"-inf": math.Inf(-1),
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err = toml.Marshal(v64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, string(actual))
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestMarshalIndentTables(t *testing.T) {
|
func TestMarshalIndentTables(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
@@ -947,6 +988,38 @@ func TestIssue678(t *testing.T) {
|
|||||||
require.Equal(t, cfg, cfg2)
|
require.Equal(t, cfg, cfg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue752(t *testing.T) {
|
||||||
|
type Fooer interface {
|
||||||
|
Foo() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
Fooer
|
||||||
|
}
|
||||||
|
|
||||||
|
c := Container{}
|
||||||
|
|
||||||
|
out, err := toml.Marshal(c)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "", string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue768(t *testing.T) {
|
||||||
|
type cfg struct {
|
||||||
|
Name string `comment:"This is a multiline comment.\nThis is line 2."`
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := toml.Marshal(&cfg{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := `# This is a multiline comment.
|
||||||
|
# This is line 2.
|
||||||
|
Name = ''
|
||||||
|
`
|
||||||
|
|
||||||
|
require.Equal(t, expected, string(out))
|
||||||
|
}
|
||||||
|
|
||||||
func TestMarshalNestedAnonymousStructs(t *testing.T) {
|
func TestMarshalNestedAnonymousStructs(t *testing.T) {
|
||||||
type Embedded struct {
|
type Embedded struct {
|
||||||
Value string `toml:"value" json:"value"`
|
Value string `toml:"value" json:"value"`
|
||||||
@@ -1011,6 +1084,37 @@ value = ''
|
|||||||
require.Equal(t, expected, string(result))
|
require.Equal(t, expected, string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLocalTime(t *testing.T) {
|
||||||
|
v := map[string]toml.LocalTime{
|
||||||
|
"a": toml.LocalTime{
|
||||||
|
Hour: 1,
|
||||||
|
Minute: 2,
|
||||||
|
Second: 3,
|
||||||
|
Nanosecond: 4,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `a = 01:02:03.000000004
|
||||||
|
`
|
||||||
|
|
||||||
|
out, err := toml.Marshal(v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expected, string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUint64Overflow(t *testing.T) {
|
||||||
|
// The TOML spec only requires implementation to provide support for the
|
||||||
|
// int64 range. To avoid generating TOML documents that would not be
|
||||||
|
// supported by standard-compliant parsers, uint64 > max int64 cannot be
|
||||||
|
// marshaled.
|
||||||
|
x := map[string]interface{}{
|
||||||
|
"foo": uint64(math.MaxInt64) + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := toml.Marshal(x)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleMarshal() {
|
func ExampleMarshal() {
|
||||||
type MyConfig struct {
|
type MyConfig struct {
|
||||||
Version int
|
Version int
|
||||||
|
|||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=0000-01-01 00:00:00")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("\"\\n\"=\"\"")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("''=0")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=0000-01-01")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=\"\"\"\\U00000000\"\"\"")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=[[{}]]")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("\"\\b\"=\"\"")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=inf")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=0000-01-01 00:00:00+00:00")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=[{}]")
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
[]byte("0=nan")
|
||||||
+29
-11
@@ -322,10 +322,12 @@ func (d *decoder) handleArrayTableCollectionLast(key ast.Iterator, v reflect.Val
|
|||||||
return v, nil
|
return v, nil
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
elemType := v.Type().Elem()
|
elemType := v.Type().Elem()
|
||||||
|
var elem reflect.Value
|
||||||
if elemType.Kind() == reflect.Interface {
|
if elemType.Kind() == reflect.Interface {
|
||||||
elemType = mapStringInterfaceType
|
elem = makeMapStringInterface()
|
||||||
|
} else {
|
||||||
|
elem = reflect.New(elemType).Elem()
|
||||||
}
|
}
|
||||||
elem := reflect.New(elemType).Elem()
|
|
||||||
elem2, err := d.handleArrayTable(key, elem)
|
elem2, err := d.handleArrayTable(key, elem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return reflect.Value{}, err
|
return reflect.Value{}, err
|
||||||
@@ -864,12 +866,27 @@ func (d *decoder) unmarshalFloat(value *ast.Node, v reflect.Value) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error {
|
const (
|
||||||
const (
|
|
||||||
maxInt = int64(^uint(0) >> 1)
|
maxInt = int64(^uint(0) >> 1)
|
||||||
minInt = -maxInt - 1
|
minInt = -maxInt - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Maximum value of uint for decoding. Currently the decoder parses the integer
|
||||||
|
// into an int64. As a result, on architectures where uint is 64 bits, the
|
||||||
|
// effective maximum uint we can decode is the maximum of int64. On
|
||||||
|
// architectures where uint is 32 bits, the maximum value we can decode is
|
||||||
|
// lower: the maximum of uint32. I didn't find a way to figure out this value at
|
||||||
|
// compile time, so it is computed during initialization.
|
||||||
|
var maxUint int64 = math.MaxInt64
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m := uint64(^uint(0))
|
||||||
|
if m < uint64(maxUint) {
|
||||||
|
maxUint = int64(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error {
|
||||||
i, err := parseInteger(value.Data)
|
i, err := parseInteger(value.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -930,7 +947,7 @@ func (d *decoder) unmarshalInteger(value *ast.Node, v reflect.Value) error {
|
|||||||
|
|
||||||
r = reflect.ValueOf(uint8(i))
|
r = reflect.ValueOf(uint8(i))
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
if i < 0 {
|
if i < 0 || i > maxUint {
|
||||||
return fmt.Errorf("toml: negative number %d does not fit in an uint", i)
|
return fmt.Errorf("toml: negative number %d does not fit in an uint", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1165,11 +1182,6 @@ func forEachField(t reflect.Type, path []int, do func(name string, path []int))
|
|||||||
fieldPath := append(path, i)
|
fieldPath := append(path, i)
|
||||||
fieldPath = fieldPath[:len(fieldPath):len(fieldPath)]
|
fieldPath = fieldPath[:len(fieldPath):len(fieldPath)]
|
||||||
|
|
||||||
if f.Anonymous {
|
|
||||||
forEachField(f.Type, fieldPath, do)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
name := f.Tag.Get("toml")
|
name := f.Tag.Get("toml")
|
||||||
if name == "-" {
|
if name == "-" {
|
||||||
continue
|
continue
|
||||||
@@ -1178,6 +1190,12 @@ func forEachField(t reflect.Type, path []int, do func(name string, path []int))
|
|||||||
if i := strings.IndexByte(name, ','); i >= 0 {
|
if i := strings.IndexByte(name, ','); i >= 0 {
|
||||||
name = name[:i]
|
name = name[:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.Anonymous && name == "" {
|
||||||
|
forEachField(f.Type, fieldPath, do)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = f.Name
|
name = f.Name
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-2
@@ -971,7 +971,7 @@ B = "data"`,
|
|||||||
"Name": "Hammer",
|
"Name": "Hammer",
|
||||||
"Sku": int64(738594937),
|
"Sku": int64(738594937),
|
||||||
},
|
},
|
||||||
map[string]interface{}(nil),
|
map[string]interface{}{},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"Name": "Nail",
|
"Name": "Nail",
|
||||||
"Sku": int64(284758393),
|
"Sku": int64(284758393),
|
||||||
@@ -1505,7 +1505,7 @@ B = "data"`,
|
|||||||
target: &map[string]interface{}{},
|
target: &map[string]interface{}{},
|
||||||
expected: &map[string]interface{}{
|
expected: &map[string]interface{}{
|
||||||
"products": []interface{}{
|
"products": []interface{}{
|
||||||
map[string]interface{}(nil),
|
map[string]interface{}{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -2380,6 +2380,49 @@ func TestIssue714(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue772(t *testing.T) {
|
||||||
|
type FileHandling struct {
|
||||||
|
FilePattern string `toml:"pattern"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
FileHandling `toml:"filehandling"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultConfigFile = []byte(`
|
||||||
|
[filehandling]
|
||||||
|
pattern = "reach-masterdev-"`)
|
||||||
|
|
||||||
|
config := Config{}
|
||||||
|
err := toml.Unmarshal(defaultConfigFile, &config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "reach-masterdev-", config.FileHandling.FilePattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue774(t *testing.T) {
|
||||||
|
type ScpData struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenConfig struct {
|
||||||
|
SCP []ScpData `toml:"scp" comment:"Array of Secure Copy Configurations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &GenConfig{}
|
||||||
|
c.SCP = []ScpData{{Host: "main.domain.com"}}
|
||||||
|
|
||||||
|
b, err := toml.Marshal(c)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := `# Array of Secure Copy Configurations
|
||||||
|
[[scp]]
|
||||||
|
Host = 'main.domain.com'
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
require.Equal(t, expected, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnmarshalDecodeErrors(t *testing.T) {
|
func TestUnmarshalDecodeErrors(t *testing.T) {
|
||||||
examples := []struct {
|
examples := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|||||||
Reference in New Issue
Block a user