增加websocket支持

This commit is contained in:
2025-09-08 13:47:13 +08:00
parent 9caedd697d
commit 7e0fd53dd3
336 changed files with 9020 additions and 20356 deletions

View File

@@ -3,5 +3,4 @@ fuzz/
cmd/tomll/tomll
cmd/tomljson/tomljson
cmd/tomltestgen/tomltestgen
dist
tests/
dist

View File

@@ -18,7 +18,6 @@ builds:
- linux_amd64
- linux_arm64
- linux_arm
- linux_riscv64
- windows_amd64
- windows_arm64
- windows_arm
@@ -38,7 +37,6 @@ builds:
- linux_amd64
- linux_arm64
- linux_arm
- linux_riscv64
- windows_amd64
- windows_arm64
- windows_arm
@@ -57,7 +55,6 @@ builds:
targets:
- linux_amd64
- linux_arm64
- linux_riscv64
- linux_arm
- windows_amd64
- windows_arm64

View File

@@ -165,22 +165,25 @@ Checklist:
### New release
1. Decide on the next version number. Use semver. Review commits since last
version to assess.
2. Tag release. For example:
1. Decide on the next version number. Use semver.
2. Generate release notes using [`gh`][gh]. Example:
```
git checkout v2
git pull
git tag v2.2.0
git push --tags
$ gh api -X POST \
-F tag_name='v2.0.0-beta.5' \
-F target_commitish='v2' \
-F previous_tag_name='v2.0.0-beta.4' \
--jq '.body' \
repos/pelletier/go-toml/releases/generate-notes
```
3. CI automatically builds a draft Github release. Review it and edit as
necessary. Look for "Other changes". That would indicate a pull request not
labeled properly. Tweak labels and pull request titles until changelog looks
good for users.
4. Check "create discussion" box, in the "Releases" category.
5. If new version is an alpha or beta only, check pre-release box.
3. Look for "Other changes". That would indicate a pull request not labeled
properly. Tweak labels and pull request titles until changelog looks good for
users.
4. [Draft new release][new-release].
5. Fill tag and target with the same value used to generate the changelog.
6. Set title to the new tag value.
7. Paste the generated changelog.
8. Check "create discussion", in the "Releases" category.
9. Check pre-release if new version is an alpha or beta.
[issues-tracker]: https://github.com/pelletier/go-toml/issues
[bug-report]: https://github.com/pelletier/go-toml/issues/new?template=bug_report.md

View File

@@ -1,7 +1,6 @@
The MIT License (MIT)
go-toml v2
Copyright (c) 2021 - 2023 Thomas Pelletier
Copyright (c) 2013 - 2022 Thomas Pelletier, Eric Anderton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -45,15 +45,16 @@ to check for typos. [See example in the documentation][strict].
### Contextualized errors
When most decoding errors occur, go-toml returns [`DecodeError`][decode-err],
When most decoding errors occur, go-toml returns [`DecodeError`][decode-err]),
which contains a human readable contextualized version of the error. For
example:
```
1| [server]
2| path = 100
| ~~~ cannot decode TOML integer into struct field toml_test.Server.Path of type string
3| port = 50
2| key1 = "value1"
3| key2 = "missing2"
| ~~~~ missing field
4| key3 = "missing3"
5| key4 = "value4"
```
[decode-err]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#DecodeError
@@ -72,35 +73,15 @@ representation.
[tlt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalTime
[tldt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDateTime
### Commented config
Since TOML is often used for configuration files, go-toml can emit documents
annotated with [comments and commented-out values][comments-example]. For
example, it can generate the following file:
```toml
# Host IP to connect to.
host = '127.0.0.1'
# Port of the remote server.
port = 4242
# Encryption parameters (optional)
# [TLS]
# cipher = 'AEAD-AES128-GCM-SHA256'
# version = 'TLS 1.3'
```
[comments-example]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Marshal-Commented
## Getting started
Given the following struct, let's see how to read it and write it as TOML:
```go
type MyConfig struct {
Version int
Name string
Tags []string
Version int
Name string
Tags []string
}
```
@@ -119,7 +100,7 @@ tags = ["go", "toml"]
var cfg MyConfig
err := toml.Unmarshal([]byte(doc), &cfg)
if err != nil {
panic(err)
panic(err)
}
fmt.Println("version:", cfg.Version)
fmt.Println("name:", cfg.Name)
@@ -140,14 +121,14 @@ as a TOML document:
```go
cfg := MyConfig{
Version: 2,
Name: "go-toml",
Tags: []string{"go", "toml"},
Version: 2,
Name: "go-toml",
Tags: []string{"go", "toml"},
}
b, err := toml.Marshal(cfg)
if err != nil {
panic(err)
panic(err)
}
fmt.Println(string(b))
@@ -175,17 +156,17 @@ the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable.
Execution time speedup compared to other Go TOML libraries:
<table>
<thead>
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
</thead>
<tbody>
<tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>2.2x</td></tr>
<tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>2.1x</td></tr>
<tr><td>Marshal/ReferenceFile/struct-2</td><td>2.2x</td><td>3.0x</td></tr>
<tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.7x</td></tr>
<tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.6x</td><td>2.7x</td></tr>
<tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.6x</td><td>5.1x</td></tr>
</tbody>
<thead>
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
</thead>
<tbody>
<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.8x</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.9x</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.4x</td><td>5.3x</td></tr>
</tbody>
</table>
<details><summary>See more</summary>
<p>The table above has the results of the most common use-cases. The table below
@@ -193,22 +174,22 @@ contains the results of all benchmarks, including unrealistic ones. It is
provided for completeness.</p>
<table>
<thead>
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
</thead>
<tbody>
<tr><td>Marshal/SimpleDocument/map-2</td><td>1.8x</td><td>2.7x</td></tr>
<tr><td>Marshal/SimpleDocument/struct-2</td><td>2.7x</td><td>3.8x</td></tr>
<tr><td>Unmarshal/SimpleDocument/map-2</td><td>3.8x</td><td>3.0x</td></tr>
<tr><td>Unmarshal/SimpleDocument/struct-2</td><td>5.6x</td><td>4.1x</td></tr>
<tr><td>UnmarshalDataset/example-2</td><td>3.0x</td><td>3.2x</td></tr>
<tr><td>UnmarshalDataset/code-2</td><td>2.3x</td><td>2.9x</td></tr>
<tr><td>UnmarshalDataset/twitter-2</td><td>2.6x</td><td>2.7x</td></tr>
<tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.2x</td><td>2.3x</td></tr>
<tr><td>UnmarshalDataset/canada-2</td><td>1.8x</td><td>1.5x</td></tr>
<tr><td>UnmarshalDataset/config-2</td><td>4.1x</td><td>2.9x</td></tr>
<tr><td>geomean</td><td>2.7x</td><td>2.8x</td></tr>
</tbody>
<thead>
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
</thead>
<tbody>
<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.7x</td><td>4.2x</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.2x</td><td>3.9x</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.3x</td><td>3.1x</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.1x</td><td>2.2x</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.3x</td><td>3.2x</td></tr>
<tr><td>[Geo mean]</td><td>2.7x</td><td>2.8x</td></tr>
</tbody>
</table>
<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>
</details>
@@ -233,24 +214,24 @@ Go-toml provides three handy command line tools:
* `tomljson`: Reads a TOML file and outputs its JSON representation.
```
$ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest
$ tomljson --help
```
```
$ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest
$ tomljson --help
```
* `jsontoml`: Reads a JSON file and outputs a TOML representation.
```
$ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest
$ jsontoml --help
```
```
$ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest
$ jsontoml --help
```
* `tomll`: Lints and reformats a TOML file.
```
$ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest
$ tomll --help
```
```
$ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest
$ tomll --help
```
### Docker image
@@ -261,7 +242,7 @@ Those tools are also available as a [Docker image][docker]. For example, to use
docker run -i ghcr.io/pelletier/go-toml:v2 tomljson < example.toml
```
Multiple versions are available on [ghcr.io][docker].
Multiple versions are availble on [ghcr.io][docker].
[docker]: https://github.com/pelletier/go-toml/pkgs/container/go-toml
@@ -293,16 +274,16 @@ element in the interface to decode the object. For example:
```go
type inner struct {
B interface{}
B interface{}
}
type doc struct {
A interface{}
A interface{}
}
d := doc{
A: inner{
B: "Before",
},
A: inner{
B: "Before",
},
}
data := `
@@ -341,7 +322,7 @@ contained in the doc is superior to the capacity of the array. For example:
```go
type doc struct {
A [2]string
A [2]string
}
d := doc{}
err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d)
@@ -516,20 +497,27 @@ is not necessary anymore.
V1 used to provide multiple struct tags: `comment`, `commented`, `multiline`,
`toml`, and `omitempty`. To behave more like the standard library, v2 has merged
`toml`, `multiline`, `commented`, and `omitempty`. For example:
`toml`, `multiline`, and `omitempty`. For example:
```go
type doc struct {
// v1
F string `toml:"field" multiline:"true" omitempty:"true" commented:"true"`
F string `toml:"field" multiline:"true" omitempty:"true"`
// v2
F string `toml:"field,multiline,omitempty,commented"`
F string `toml:"field,multiline,omitempty"`
}
```
Has a result, the `Encoder.SetTag*` methods have been removed, as there is just
one tag now.
#### `commented` tag has been removed
There is no replacement for the `commented` tag. This feature would be better
suited in a proper document model for go-toml v2, which has been [cut from
scope][nodoc] at the moment.
#### `Encoder.ArraysWithOneElementPerLine` has been renamed
The new name is `Encoder.SetArraysMultiline`. The behavior should be the same.
@@ -565,11 +553,10 @@ complete solutions exist out there.
## Versioning
Expect for parts explicitely marked otherwise, go-toml follows [Semantic
Versioning](https://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)).
Go-toml follows [Semantic Versioning](https://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

View File

@@ -2,6 +2,9 @@
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ---------- | ------------------ |
| Latest 2.x | :white_check_mark: |

View File

@@ -77,9 +77,8 @@ cover() {
pushd "$dir"
go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out.tmp ./...
grep -Ev '(fuzz|testsuite|tomltestgen|gotoml-test-decoder|gotoml-test-encoder)' coverage.out.tmp > coverage.out
cat coverage.out.tmp | grep -v fuzz | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out
go tool cover -func=coverage.out
echo "Coverage profile for ${branch}: ${dir}/coverage.out" >&2
popd
if [ "${branch}" != "HEAD" ]; then
@@ -152,7 +151,7 @@ bench() {
fi
export GOMAXPROCS=2
go test '-bench=^Benchmark(Un)?[mM]arshal' -count=10 -run=Nothing ./... | tee "${out}"
nice -n -19 taskset --cpu-list 0,1 go test '-bench=^Benchmark(Un)?[mM]arshal' -count=5 -run=Nothing ./... | tee "${out}"
popd
if [ "${branch}" != "HEAD" ]; then
@@ -161,12 +160,10 @@ bench() {
}
fmktemp() {
if mktemp --version &> /dev/null; then
# GNU
mktemp --suffix=-$1
if mktemp --version|grep GNU >/dev/null; then
mktemp --suffix=-$1;
else
# BSD
mktemp -t $1
mktemp -t $1;
fi
}
@@ -186,14 +183,12 @@ with open(sys.argv[1]) as f:
lines.append(line.split(','))
results = []
for line in reversed(lines[2:]):
if len(line) < 8 or line[0] == "":
continue
for line in reversed(lines[1:]):
v2 = float(line[1])
results.append([
line[0].replace("-32", ""),
"%.1fx" % (float(line[3])/v2), # v1
"%.1fx" % (float(line[7])/v2), # bs
"%.1fx" % (float(line[5])/v2), # bs
])
# move geomean to the end
results.append(results[0])
@@ -264,10 +259,10 @@ benchmark() {
if [ "$1" = "-html" ]; then
tmpcsv=`fmktemp csv`
benchstat -format csv go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv
benchstat -csv -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv
benchstathtml $tmpcsv
else
benchstat go-toml-v2.txt go-toml-v1.txt bs-toml.txt
benchstat -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt
fi
rm -f go-toml-v2.txt go-toml-v1.txt bs-toml.txt

View File

@@ -318,7 +318,7 @@ func parseFloat(b []byte) (float64, error) {
if cleaned[0] == '+' || cleaned[0] == '-' {
start = 1
}
if cleaned[start] == '0' && len(cleaned) > start+1 && isDigit(cleaned[start+1]) {
if cleaned[start] == '0' && isDigit(cleaned[start+1]) {
return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes")
}

View File

@@ -57,11 +57,7 @@ type SeenTracker struct {
currentIdx int
}
var pool = sync.Pool{
New: func() interface{} {
return &SeenTracker{}
},
}
var pool sync.Pool
func (s *SeenTracker) reset() {
// Always contains a root element at index 0.
@@ -153,9 +149,8 @@ func (s *SeenTracker) setExplicitFlag(parentIdx int) {
// CheckExpression takes a top-level node and checks that it does not contain
// keys that have been seen in previous calls, and validates that types are
// consistent. It returns true if it is the first time this node's key is seen.
// Useful to clear array tables on first use.
func (s *SeenTracker) CheckExpression(node *unstable.Node) (bool, error) {
// consistent.
func (s *SeenTracker) CheckExpression(node *unstable.Node) error {
if s.entries == nil {
s.reset()
}
@@ -171,7 +166,7 @@ func (s *SeenTracker) CheckExpression(node *unstable.Node) (bool, error) {
}
}
func (s *SeenTracker) checkTable(node *unstable.Node) (bool, error) {
func (s *SeenTracker) checkTable(node *unstable.Node) error {
if s.currentIdx >= 0 {
s.setExplicitFlag(s.currentIdx)
}
@@ -197,7 +192,7 @@ func (s *SeenTracker) checkTable(node *unstable.Node) (bool, error) {
} else {
entry := s.entries[idx]
if entry.kind == valueKind {
return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
}
}
parentIdx = idx
@@ -206,27 +201,25 @@ func (s *SeenTracker) checkTable(node *unstable.Node) (bool, error) {
k := it.Node().Data
idx := s.find(parentIdx, k)
first := false
if idx >= 0 {
kind := s.entries[idx].kind
if kind != tableKind {
return false, fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind)
return fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind)
}
if s.entries[idx].explicit {
return false, fmt.Errorf("toml: table %s already exists", string(k))
return fmt.Errorf("toml: table %s already exists", string(k))
}
s.entries[idx].explicit = true
} else {
idx = s.create(parentIdx, k, tableKind, true, false)
first = true
}
s.currentIdx = idx
return first, nil
return nil
}
func (s *SeenTracker) checkArrayTable(node *unstable.Node) (bool, error) {
func (s *SeenTracker) checkArrayTable(node *unstable.Node) error {
if s.currentIdx >= 0 {
s.setExplicitFlag(s.currentIdx)
}
@@ -249,7 +242,7 @@ func (s *SeenTracker) checkArrayTable(node *unstable.Node) (bool, error) {
} else {
entry := s.entries[idx]
if entry.kind == valueKind {
return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
}
}
@@ -259,23 +252,22 @@ func (s *SeenTracker) checkArrayTable(node *unstable.Node) (bool, error) {
k := it.Node().Data
idx := s.find(parentIdx, k)
firstTime := idx < 0
if firstTime {
idx = s.create(parentIdx, k, arrayTableKind, true, false)
} else {
if idx >= 0 {
kind := s.entries[idx].kind
if kind != arrayTableKind {
return false, fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", kind, string(k))
return fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", kind, string(k))
}
s.clear(idx)
} else {
idx = s.create(parentIdx, k, arrayTableKind, true, false)
}
s.currentIdx = idx
return firstTime, nil
return nil
}
func (s *SeenTracker) checkKeyValue(node *unstable.Node) (bool, error) {
func (s *SeenTracker) checkKeyValue(node *unstable.Node) error {
parentIdx := s.currentIdx
it := node.Key()
@@ -289,11 +281,11 @@ func (s *SeenTracker) checkKeyValue(node *unstable.Node) (bool, error) {
} else {
entry := s.entries[idx]
if it.IsLast() {
return false, fmt.Errorf("toml: key %s is already defined", string(k))
return fmt.Errorf("toml: key %s is already defined", string(k))
} else if entry.kind != tableKind {
return false, fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind)
} else if entry.explicit {
return false, fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k))
return fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k))
}
}
@@ -311,39 +303,45 @@ func (s *SeenTracker) checkKeyValue(node *unstable.Node) (bool, error) {
return s.checkArray(value)
}
return false, nil
return nil
}
func (s *SeenTracker) checkArray(node *unstable.Node) (first bool, err error) {
func (s *SeenTracker) checkArray(node *unstable.Node) error {
it := node.Children()
for it.Next() {
n := it.Node()
switch n.Kind {
case unstable.InlineTable:
first, err = s.checkInlineTable(n)
err := s.checkInlineTable(n)
if err != nil {
return false, err
return err
}
case unstable.Array:
first, err = s.checkArray(n)
err := s.checkArray(n)
if err != nil {
return false, err
return err
}
}
}
return first, nil
return nil
}
func (s *SeenTracker) checkInlineTable(node *unstable.Node) (first bool, err error) {
func (s *SeenTracker) checkInlineTable(node *unstable.Node) error {
if pool.New == nil {
pool.New = func() interface{} {
return &SeenTracker{}
}
}
s = pool.Get().(*SeenTracker)
s.reset()
it := node.Children()
for it.Next() {
n := it.Node()
first, err = s.checkKeyValue(n)
err := s.checkKeyValue(n)
if err != nil {
return false, err
return err
}
}
@@ -354,5 +352,5 @@ func (s *SeenTracker) checkInlineTable(node *unstable.Node) (first bool, err err
// redefinition of its keys: check* functions cannot walk into
// a value.
pool.Put(s)
return first, nil
return nil
}

View File

@@ -3,7 +3,6 @@ package toml
import (
"bytes"
"encoding"
"encoding/json"
"fmt"
"io"
"math"
@@ -38,11 +37,10 @@ type Encoder struct {
w io.Writer
// global settings
tablesInline bool
arraysMultiline bool
indentSymbol string
indentTables bool
marshalJsonNumbers bool
tablesInline bool
arraysMultiline bool
indentSymbol string
indentTables bool
}
// NewEncoder returns a new Encoder that writes to w.
@@ -89,17 +87,6 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
return enc
}
// SetMarshalJsonNumbers forces the encoder to serialize `json.Number` as a
// float or integer instead of relying on TextMarshaler to emit a string.
//
// *Unstable:* This method does not follow the compatibility guarantees of
// semver. It can be changed or removed without a new major version being
// issued.
func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder {
enc.marshalJsonNumbers = indent
return enc
}
// Encode writes a TOML representation of v to the stream.
//
// If v cannot be represented to TOML it returns an error.
@@ -161,9 +148,6 @@ func (enc *Encoder) SetMarshalJsonNumbers(indent bool) *Encoder {
//
// The "omitempty" option prevents empty values or groups from being emitted.
//
// The "commented" option prefixes the value and all its children with a comment
// symbol.
//
// 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
// inline tables. For array tables, the comment is only present before the first
@@ -196,7 +180,6 @@ func (enc *Encoder) Encode(v interface{}) error {
type valueOptions struct {
multiline bool
omitempty bool
commented bool
comment string
}
@@ -222,9 +205,6 @@ type encoderCtx struct {
// Indentation level
indent int
// Prefix the current value with a comment.
commented bool
// Options coming from struct tags
options valueOptions
}
@@ -265,18 +245,6 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
return append(b, x.String()...), nil
case LocalDateTime:
return append(b, x.String()...), nil
case json.Number:
if enc.marshalJsonNumbers {
if x == "" { /// Useful zero value.
return append(b, "0"...), nil
} else if v, err := x.Int64(); err == nil {
return enc.encode(b, ctx, reflect.ValueOf(v))
} else if f, err := x.Float64(); err == nil {
return enc.encode(b, ctx, reflect.ValueOf(f))
} else {
return nil, fmt.Errorf("toml: unable to convert %q to int64 or float64", x)
}
}
}
hasTextMarshaler := v.Type().Implements(textMarshalerType)
@@ -305,7 +273,7 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
return enc.encodeMap(b, ctx, v)
case reflect.Struct:
return enc.encodeStruct(b, ctx, v)
case reflect.Slice, reflect.Array:
case reflect.Slice:
return enc.encodeSlice(b, ctx, v)
case reflect.Interface:
if v.IsNil() {
@@ -389,7 +357,6 @@ func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v r
if !ctx.inline {
b = enc.encodeComment(ctx.indent, options.comment, b)
b = enc.commented(ctx.commented, b)
b = enc.indent(ctx.indent, b)
}
@@ -411,13 +378,6 @@ func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v r
return b, nil
}
func (enc *Encoder) commented(commented bool, b []byte) []byte {
if commented {
return append(b, "# "...)
}
return b
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Struct:
@@ -566,8 +526,6 @@ func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error)
b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
b = enc.commented(ctx.commented, b)
b = enc.indent(ctx.indent, b)
b = append(b, '[')
@@ -732,8 +690,6 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
if fieldType.Anonymous {
if fieldType.Type.Kind() == reflect.Struct {
walkStruct(ctx, t, f)
} else if fieldType.Type.Kind() == reflect.Pointer && !f.IsNil() && f.Elem().Kind() == reflect.Struct {
walkStruct(ctx, t, f.Elem())
}
continue
} else {
@@ -748,7 +704,6 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) {
options := valueOptions{
multiline: opts.multiline,
omitempty: opts.omitempty,
commented: opts.commented,
comment: fieldType.Tag.Get("comment"),
}
@@ -808,7 +763,6 @@ type tagOptions struct {
multiline bool
inline bool
omitempty bool
commented bool
}
func parseTag(tag string) (string, tagOptions) {
@@ -836,8 +790,6 @@ func parseTag(tag string) (string, tagOptions) {
opts.inline = true
case "omitempty":
opts.omitempty = true
case "commented":
opts.commented = true
}
}
@@ -873,10 +825,8 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
hasNonEmptyKV = true
ctx.setKey(kv.Key)
ctx2 := ctx
ctx2.commented = kv.Options.commented || ctx2.commented
b, err = enc.encodeKv(b, ctx2, kv.Options, kv.Value)
b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value)
if err != nil {
return nil, err
}
@@ -901,10 +851,8 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
ctx.setKey(table.Key)
ctx.options = table.Options
ctx2 := ctx
ctx2.commented = ctx2.commented || ctx.options.commented
b, err = enc.encode(b, ctx2, table.Value)
b, err = enc.encode(b, ctx, table.Value)
if err != nil {
return nil, err
}
@@ -982,7 +930,7 @@ func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool {
return willConvertToTableOrArrayTable(ctx, v.Elem())
}
if t.Kind() == reflect.Slice || t.Kind() == reflect.Array {
if t.Kind() == reflect.Slice {
if v.Len() == 0 {
// An empty slice should be a kv = [].
return false
@@ -1022,13 +970,6 @@ func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.
ctx.shiftKey()
scratch := make([]byte, 0, 64)
scratch = enc.commented(ctx.commented, scratch)
if enc.indentTables {
scratch = enc.indent(ctx.indent, scratch)
}
scratch = append(scratch, "[["...)
for i, k := range ctx.parentKey {
@@ -1044,10 +985,6 @@ func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.
b = enc.encodeComment(ctx.indent, ctx.options.comment, b)
if enc.indentTables {
ctx.indent++
}
for i := 0; i < v.Len(); i++ {
if i != 0 {
b = append(b, "\n"...)

View File

@@ -35,9 +35,6 @@ type Decoder struct {
// global settings
strict bool
// toggles unmarshaler interface
unmarshalerInterface bool
}
// NewDecoder creates a new Decoder that will read from r.
@@ -57,24 +54,6 @@ func (d *Decoder) DisallowUnknownFields() *Decoder {
return d
}
// EnableUnmarshalerInterface allows to enable unmarshaler interface.
//
// With this feature enabled, types implementing the unstable/Unmarshaler
// interface can be decoded from any structure of the document. It allows types
// that don't have a straightfoward TOML representation to provide their own
// decoding logic.
//
// Currently, types can only decode from a single value. Tables and array tables
// are not supported.
//
// *Unstable:* This method does not follow the compatibility guarantees of
// semver. It can be changed or removed without a new major version being
// issued.
func (d *Decoder) EnableUnmarshalerInterface() *Decoder {
d.unmarshalerInterface = true
return d
}
// Decode the whole content of r into v.
//
// By default, values in the document that don't exist in the target Go value
@@ -129,7 +108,6 @@ func (d *Decoder) Decode(v interface{}) error {
strict: strict{
Enabled: d.strict,
},
unmarshalerInterface: d.unmarshalerInterface,
}
return dec.FromParser(v)
@@ -149,10 +127,6 @@ type decoder struct {
// need to be skipped.
skipUntilTable bool
// Flag indicating that the current array/slice table should be cleared because
// it is the first encounter of an array table.
clearArrayTable bool
// Tracks position in Go arrays.
// This is used when decoding [[array tables]] into Go arrays. Given array
// tables are separate TOML expression, we need to keep track of where we
@@ -165,9 +139,6 @@ type decoder struct {
// Strict mode
strict strict
// Flag that enables/disables unmarshaler interface.
unmarshalerInterface bool
// Current context for the error.
errorContext *errorContext
}
@@ -178,16 +149,12 @@ type errorContext struct {
}
func (d *decoder) typeMismatchError(toml string, target reflect.Type) error {
return fmt.Errorf("toml: %s", d.typeMismatchString(toml, target))
}
func (d *decoder) typeMismatchString(toml string, target reflect.Type) string {
if d.errorContext != nil && d.errorContext.Struct != nil {
ctx := d.errorContext
f := ctx.Struct.FieldByIndex(ctx.Field)
return fmt.Sprintf("cannot decode TOML %s into struct field %s.%s of type %s", toml, ctx.Struct, f.Name, f.Type)
return fmt.Errorf("toml: cannot decode TOML %s into struct field %s.%s of type %s", toml, ctx.Struct, f.Name, f.Type)
}
return fmt.Sprintf("cannot decode TOML %s into a Go value of type %s", toml, target)
return fmt.Errorf("toml: cannot decode TOML %s into a Go value of type %s", toml, target)
}
func (d *decoder) expr() *unstable.Node {
@@ -275,10 +242,9 @@ Rules for the unmarshal code:
func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) error {
var x reflect.Value
var err error
var first bool // used for to clear array tables on first use
if !(d.skipUntilTable && expr.Kind == unstable.KeyValue) {
first, err = d.seen.CheckExpression(expr)
err = d.seen.CheckExpression(expr)
if err != nil {
return err
}
@@ -297,7 +263,6 @@ func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) err
case unstable.ArrayTable:
d.skipUntilTable = false
d.strict.EnterArrayTable(expr)
d.clearArrayTable = first
x, err = d.handleArrayTable(expr.Key(), v)
default:
panic(fmt.Errorf("parser should not permit expression of kind %s at document root", expr.Kind))
@@ -338,10 +303,6 @@ func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflec
reflect.Copy(nelem, elem)
elem = nelem
}
if d.clearArrayTable && elem.Len() > 0 {
elem.SetLen(0)
d.clearArrayTable = false
}
}
return d.handleArrayTableCollectionLast(key, elem)
case reflect.Ptr:
@@ -360,10 +321,6 @@ func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflec
return v, nil
case reflect.Slice:
if d.clearArrayTable && v.Len() > 0 {
v.SetLen(0)
d.clearArrayTable = false
}
elemType := v.Type().Elem()
var elem reflect.Value
if elemType.Kind() == reflect.Interface {
@@ -615,7 +572,7 @@ func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) {
break
}
_, err := d.seen.CheckExpression(expr)
err := d.seen.CheckExpression(expr)
if err != nil {
return reflect.Value{}, err
}
@@ -673,14 +630,6 @@ func (d *decoder) handleValue(value *unstable.Node, v reflect.Value) error {
v = initAndDereferencePointer(v)
}
if d.unmarshalerInterface {
if v.CanAddr() && v.Addr().CanInterface() {
if outi, ok := v.Addr().Interface().(unstable.Unmarshaler); ok {
return outi.UnmarshalTOML(value)
}
}
}
ok, err := d.tryTextUnmarshaler(value, v)
if ok || err != nil {
return err
@@ -1014,7 +963,7 @@ func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error
case reflect.Interface:
r = reflect.ValueOf(i)
default:
return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("integer", v.Type()))
return d.typeMismatchError("integer", v.Type())
}
if !r.Type().AssignableTo(v.Type()) {
@@ -1033,7 +982,7 @@ func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error {
case reflect.Interface:
v.Set(reflect.ValueOf(string(value.Data)))
default:
return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("string", v.Type()))
return unstable.NewParserError(d.p.Raw(value.Raw), "cannot store TOML string into a Go %s", v.Kind())
}
return nil
@@ -1144,9 +1093,9 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node
f := fieldByIndex(v, path)
if !f.CanAddr() {
// If the field is not addressable, need to take a slower path and
// make a copy of the struct itself to a new location.
if !f.CanSet() {
// If the field is not settable, need to take a slower path and make a copy of
// the struct itself to a new location.
nvp := reflect.New(v.Type())
nvp.Elem().Set(v)
v = nvp.Elem()
@@ -1221,10 +1170,10 @@ func initAndDereferencePointer(v reflect.Value) reflect.Value {
// Same as reflect.Value.FieldByIndex, but creates pointers if needed.
func fieldByIndex(v reflect.Value, path []int) reflect.Value {
for _, x := range path {
for i, x := range path {
v = v.Field(x)
if v.Kind() == reflect.Ptr {
if i < len(path)-1 && v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}

View File

@@ -1013,7 +1013,6 @@ func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error)
return p.builder.Push(Node{
Kind: Float,
Data: b[:3],
Raw: p.Range(b[:3]),
}), b[3:], nil
case 'n':
if !scanFollowsNan(b) {
@@ -1023,7 +1022,6 @@ func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error)
return p.builder.Push(Node{
Kind: Float,
Data: b[:3],
Raw: p.Range(b[:3]),
}), b[3:], nil
case '+', '-':
return p.scanIntOrFloat(b)
@@ -1148,7 +1146,6 @@ func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) {
return p.builder.Push(Node{
Kind: Integer,
Data: b[:i],
Raw: p.Range(b[:i]),
}), b[i:], nil
}
@@ -1172,7 +1169,6 @@ func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) {
return p.builder.Push(Node{
Kind: Float,
Data: b[:i+3],
Raw: p.Range(b[:i+3]),
}), b[i+3:], nil
}
@@ -1184,7 +1180,6 @@ func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) {
return p.builder.Push(Node{
Kind: Float,
Data: b[:i+3],
Raw: p.Range(b[:i+3]),
}), b[i+3:], nil
}
@@ -1207,7 +1202,6 @@ func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) {
return p.builder.Push(Node{
Kind: kind,
Data: b[:i],
Raw: p.Range(b[:i]),
}), b[i:], nil
}