Bump github.com/BurntSushi/toml from 1.1.0 to 1.2.0
Bumps [github.com/BurntSushi/toml](https://github.com/BurntSushi/toml) from 1.1.0 to 1.2.0. - [Release notes](https://github.com/BurntSushi/toml/releases) - [Commits](https://github.com/BurntSushi/toml/compare/v1.1.0...v1.2.0) --- updated-dependencies: - dependency-name: github.com/BurntSushi/toml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
		
							parent
							
								
									712267ee20
								
							
						
					
					
						commit
						138d185cc5
					
				
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							| 
						 | 
				
			
			@ -3,7 +3,7 @@ module github.com/containers/podman/v4
 | 
			
		|||
go 1.16
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/BurntSushi/toml v1.1.0
 | 
			
		||||
	github.com/BurntSushi/toml v1.2.0
 | 
			
		||||
	github.com/blang/semver/v4 v4.0.0
 | 
			
		||||
	github.com/buger/goterm v1.0.4
 | 
			
		||||
	github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										3
									
								
								go.sum
								
								
								
								
							| 
						 | 
				
			
			@ -102,8 +102,9 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
 | 
			
		|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
			
		||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
			
		||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
 | 
			
		||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
			
		||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
 | 
			
		||||
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 | 
			
		||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,2 +1,2 @@
 | 
			
		|||
toml.test
 | 
			
		||||
/toml.test
 | 
			
		||||
/toml-test
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
 | 
			
		||||
reflection interface similar to Go's standard library `json` and `xml`
 | 
			
		||||
packages.
 | 
			
		||||
reflection interface similar to Go's standard library `json` and `xml` packages.
 | 
			
		||||
 | 
			
		||||
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +9,7 @@ See the [releases page](https://github.com/BurntSushi/toml/releases) for a
 | 
			
		|||
changelog; this information is also in the git tag annotations (e.g. `git show
 | 
			
		||||
v0.4.0`).
 | 
			
		||||
 | 
			
		||||
This library requires Go 1.13 or newer; install it with:
 | 
			
		||||
This library requires Go 1.13 or newer; add it to your go.mod with:
 | 
			
		||||
 | 
			
		||||
    % go get github.com/BurntSushi/toml@latest
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,16 +18,7 @@ It also comes with a TOML validator CLI tool:
 | 
			
		|||
    % go install github.com/BurntSushi/toml/cmd/tomlv@latest
 | 
			
		||||
    % tomlv some-toml-file.toml
 | 
			
		||||
 | 
			
		||||
### Testing
 | 
			
		||||
This package passes all tests in [toml-test] for both the decoder and the
 | 
			
		||||
encoder.
 | 
			
		||||
 | 
			
		||||
[toml-test]: https://github.com/BurntSushi/toml-test
 | 
			
		||||
 | 
			
		||||
### Examples
 | 
			
		||||
This package works similar to how the Go standard library handles XML and JSON.
 | 
			
		||||
Namely, data is loaded into Go values via reflection.
 | 
			
		||||
 | 
			
		||||
For the simplest example, consider some TOML file as just a list of keys and
 | 
			
		||||
values:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +30,7 @@ Perfection = [ 6, 28, 496, 8128 ]
 | 
			
		|||
DOB = 1987-07-05T05:45:00Z
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Which could be defined in Go as:
 | 
			
		||||
Which can be decoded with:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type Config struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,20 +38,15 @@ type Config struct {
 | 
			
		|||
	Cats       []string
 | 
			
		||||
	Pi         float64
 | 
			
		||||
	Perfection []int
 | 
			
		||||
	DOB        time.Time // requires `import time`
 | 
			
		||||
	DOB        time.Time
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And then decoded with:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
var conf Config
 | 
			
		||||
_, err := toml.Decode(tomlData, &conf)
 | 
			
		||||
// handle error
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can also use struct tags if your struct field name doesn't map to a TOML
 | 
			
		||||
key value directly:
 | 
			
		||||
You can also use struct tags if your struct field name doesn't map to a TOML key
 | 
			
		||||
value directly:
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
some_key_NAME = "wat"
 | 
			
		||||
| 
						 | 
				
			
			@ -73,139 +58,63 @@ type TOML struct {
 | 
			
		|||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Beware that like other most other decoders **only exported fields** are
 | 
			
		||||
considered when encoding and decoding; private fields are silently ignored.
 | 
			
		||||
Beware that like other decoders **only exported fields** are considered when
 | 
			
		||||
encoding and decoding; private fields are silently ignored.
 | 
			
		||||
 | 
			
		||||
### Using the `Marshaler` and `encoding.TextUnmarshaler` interfaces
 | 
			
		||||
Here's an example that automatically parses duration strings into
 | 
			
		||||
`time.Duration` values:
 | 
			
		||||
Here's an example that automatically parses values in a `mail.Address`:
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
[[song]]
 | 
			
		||||
name = "Thunder Road"
 | 
			
		||||
duration = "4m49s"
 | 
			
		||||
 | 
			
		||||
[[song]]
 | 
			
		||||
name = "Stairway to Heaven"
 | 
			
		||||
duration = "8m03s"
 | 
			
		||||
contacts = [
 | 
			
		||||
    "Donald Duck <donald@duckburg.com>",
 | 
			
		||||
    "Scrooge McDuck <scrooge@duckburg.com>",
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Which can be decoded with:
 | 
			
		||||
Can be decoded with:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type song struct {
 | 
			
		||||
	Name     string
 | 
			
		||||
	Duration duration
 | 
			
		||||
}
 | 
			
		||||
type songs struct {
 | 
			
		||||
	Song []song
 | 
			
		||||
}
 | 
			
		||||
var favorites songs
 | 
			
		||||
if _, err := toml.Decode(blob, &favorites); err != nil {
 | 
			
		||||
	log.Fatal(err)
 | 
			
		||||
// Create address type which satisfies the encoding.TextUnmarshaler interface.
 | 
			
		||||
type address struct {
 | 
			
		||||
	*mail.Address
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for _, s := range favorites.Song {
 | 
			
		||||
	fmt.Printf("%s (%s)\n", s.Name, s.Duration)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And you'll also need a `duration` type that satisfies the
 | 
			
		||||
`encoding.TextUnmarshaler` interface:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type duration struct {
 | 
			
		||||
	time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *duration) UnmarshalText(text []byte) error {
 | 
			
		||||
func (a *address) UnmarshalText(text []byte) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	d.Duration, err = time.ParseDuration(string(text))
 | 
			
		||||
	a.Address, err = mail.ParseAddress(string(text))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode it.
 | 
			
		||||
func decode() {
 | 
			
		||||
	blob := `
 | 
			
		||||
		contacts = [
 | 
			
		||||
			"Donald Duck <donald@duckburg.com>",
 | 
			
		||||
			"Scrooge McDuck <scrooge@duckburg.com>",
 | 
			
		||||
		]
 | 
			
		||||
	`
 | 
			
		||||
 | 
			
		||||
	var contacts struct {
 | 
			
		||||
		Contacts []address
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := toml.Decode(blob, &contacts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range contacts.Contacts {
 | 
			
		||||
		fmt.Printf("%#v\n", c.Address)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Output:
 | 
			
		||||
	// &mail.Address{Name:"Donald Duck", Address:"donald@duckburg.com"}
 | 
			
		||||
	// &mail.Address{Name:"Scrooge McDuck", Address:"scrooge@duckburg.com"}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To target TOML specifically you can implement `UnmarshalTOML` TOML interface in
 | 
			
		||||
a similar way.
 | 
			
		||||
 | 
			
		||||
### More complex usage
 | 
			
		||||
Here's an example of how to load the example from the official spec page:
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
# This is a TOML document. Boom.
 | 
			
		||||
 | 
			
		||||
title = "TOML Example"
 | 
			
		||||
 | 
			
		||||
[owner]
 | 
			
		||||
name = "Tom Preston-Werner"
 | 
			
		||||
organization = "GitHub"
 | 
			
		||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
 | 
			
		||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
 | 
			
		||||
 | 
			
		||||
[database]
 | 
			
		||||
server = "192.168.1.1"
 | 
			
		||||
ports = [ 8001, 8001, 8002 ]
 | 
			
		||||
connection_max = 5000
 | 
			
		||||
enabled = true
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
  # You can indent as you please. Tabs or spaces. TOML don't care.
 | 
			
		||||
  [servers.alpha]
 | 
			
		||||
  ip = "10.0.0.1"
 | 
			
		||||
  dc = "eqdc10"
 | 
			
		||||
 | 
			
		||||
  [servers.beta]
 | 
			
		||||
  ip = "10.0.0.2"
 | 
			
		||||
  dc = "eqdc10"
 | 
			
		||||
 | 
			
		||||
[clients]
 | 
			
		||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
 | 
			
		||||
 | 
			
		||||
# Line breaks are OK when inside arrays
 | 
			
		||||
hosts = [
 | 
			
		||||
  "alpha",
 | 
			
		||||
  "omega"
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And the corresponding Go types are:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type tomlConfig struct {
 | 
			
		||||
	Title   string
 | 
			
		||||
	Owner   ownerInfo
 | 
			
		||||
	DB      database `toml:"database"`
 | 
			
		||||
	Servers map[string]server
 | 
			
		||||
	Clients clients
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ownerInfo struct {
 | 
			
		||||
	Name string
 | 
			
		||||
	Org  string `toml:"organization"`
 | 
			
		||||
	Bio  string
 | 
			
		||||
	DOB  time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type database struct {
 | 
			
		||||
	Server  string
 | 
			
		||||
	Ports   []int
 | 
			
		||||
	ConnMax int `toml:"connection_max"`
 | 
			
		||||
	Enabled bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type server struct {
 | 
			
		||||
	IP string
 | 
			
		||||
	DC string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clients struct {
 | 
			
		||||
	Data  [][]interface{}
 | 
			
		||||
	Hosts []string
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Note that a case insensitive match will be tried if an exact match can't be
 | 
			
		||||
found.
 | 
			
		||||
 | 
			
		||||
A working example of the above can be found in `_example/example.{go,toml}`.
 | 
			
		||||
See the [`_example/`](/_example) directory for a more complex example.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,13 +3,16 @@ package toml
 | 
			
		|||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Unmarshaler is the interface implemented by objects that can unmarshal a
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +21,7 @@ type Unmarshaler interface {
 | 
			
		|||
	UnmarshalTOML(interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
 | 
			
		||||
// Unmarshal decodes the contents of `data` in TOML format into a pointer `v`.
 | 
			
		||||
func Unmarshal(data []byte, v interface{}) error {
 | 
			
		||||
	_, err := NewDecoder(bytes.NewReader(data)).Decode(v)
 | 
			
		||||
	return err
 | 
			
		||||
| 
						 | 
				
			
			@ -75,6 +78,9 @@ const (
 | 
			
		|||
// TOML datetimes correspond to Go time.Time values. Local datetimes are parsed
 | 
			
		||||
// in the local timezone.
 | 
			
		||||
//
 | 
			
		||||
// time.Duration types are treated as nanoseconds if the TOML value is an
 | 
			
		||||
// integer, or they're parsed with time.ParseDuration() if they're strings.
 | 
			
		||||
//
 | 
			
		||||
// All other TOML types (float, string, int, bool and array) correspond to the
 | 
			
		||||
// obvious Go types.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +88,7 @@ const (
 | 
			
		|||
// interface, in which case any primitive TOML value (floats, strings, integers,
 | 
			
		||||
// booleans, datetimes) will be converted to a []byte and given to the value's
 | 
			
		||||
// UnmarshalText method. See the Unmarshaler example for a demonstration with
 | 
			
		||||
// time duration strings.
 | 
			
		||||
// email addresses.
 | 
			
		||||
//
 | 
			
		||||
// Key mapping
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +117,7 @@ func NewDecoder(r io.Reader) *Decoder {
 | 
			
		|||
var (
 | 
			
		||||
	unmarshalToml = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
 | 
			
		||||
	unmarshalText = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
 | 
			
		||||
	primitiveType = reflect.TypeOf((*Primitive)(nil)).Elem()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Decode TOML data in to the pointer `v`.
 | 
			
		||||
| 
						 | 
				
			
			@ -122,10 +129,10 @@ func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
 | 
			
		|||
			s = "%v"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return MetaData{}, e("cannot decode to non-pointer "+s, reflect.TypeOf(v))
 | 
			
		||||
		return MetaData{}, fmt.Errorf("toml: cannot decode to non-pointer "+s, reflect.TypeOf(v))
 | 
			
		||||
	}
 | 
			
		||||
	if rv.IsNil() {
 | 
			
		||||
		return MetaData{}, e("cannot decode to nil value of %q", reflect.TypeOf(v))
 | 
			
		||||
		return MetaData{}, fmt.Errorf("toml: cannot decode to nil value of %q", reflect.TypeOf(v))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if this is a supported type: struct, map, interface{}, or something
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +142,7 @@ func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
 | 
			
		|||
	if rv.Kind() != reflect.Struct && rv.Kind() != reflect.Map &&
 | 
			
		||||
		!(rv.Kind() == reflect.Interface && rv.NumMethod() == 0) &&
 | 
			
		||||
		!rt.Implements(unmarshalToml) && !rt.Implements(unmarshalText) {
 | 
			
		||||
		return MetaData{}, e("cannot decode to type %s", rt)
 | 
			
		||||
		return MetaData{}, fmt.Errorf("toml: cannot decode to type %s", rt)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: parser should read from io.Reader? Or at the very least, make it
 | 
			
		||||
| 
						 | 
				
			
			@ -152,10 +159,11 @@ func (dec *Decoder) Decode(v interface{}) (MetaData, error) {
 | 
			
		|||
 | 
			
		||||
	md := MetaData{
 | 
			
		||||
		mapping: p.mapping,
 | 
			
		||||
		types:   p.types,
 | 
			
		||||
		keyInfo: p.keyInfo,
 | 
			
		||||
		keys:    p.ordered,
 | 
			
		||||
		decoded: make(map[string]struct{}, len(p.ordered)),
 | 
			
		||||
		context: nil,
 | 
			
		||||
		data:    data,
 | 
			
		||||
	}
 | 
			
		||||
	return md, md.unify(p.mapping, rv)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +193,7 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
 | 
			
		|||
func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
 | 
			
		||||
	// Special case. Look for a `Primitive` value.
 | 
			
		||||
	// TODO: #76 would make this superfluous after implemented.
 | 
			
		||||
	if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
 | 
			
		||||
	if rv.Type() == primitiveType {
 | 
			
		||||
		// Save the undecoded data and the key context into the primitive
 | 
			
		||||
		// value.
 | 
			
		||||
		context := make(Key, len(md.context))
 | 
			
		||||
| 
						 | 
				
			
			@ -197,17 +205,14 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Special case. Unmarshaler Interface support.
 | 
			
		||||
	if rv.CanAddr() {
 | 
			
		||||
		if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
 | 
			
		||||
			return v.UnmarshalTOML(data)
 | 
			
		||||
		}
 | 
			
		||||
	rvi := rv.Interface()
 | 
			
		||||
	if v, ok := rvi.(Unmarshaler); ok {
 | 
			
		||||
		return v.UnmarshalTOML(data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Special case. Look for a value satisfying the TextUnmarshaler interface.
 | 
			
		||||
	if v, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
 | 
			
		||||
	if v, ok := rvi.(encoding.TextUnmarshaler); ok {
 | 
			
		||||
		return md.unifyText(data, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO:
 | 
			
		||||
	// The behavior here is incorrect whenever a Go type satisfies the
 | 
			
		||||
	// encoding.TextUnmarshaler interface but also corresponds to a TOML hash or
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +223,6 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
 | 
			
		|||
 | 
			
		||||
	k := rv.Kind()
 | 
			
		||||
 | 
			
		||||
	// laziness
 | 
			
		||||
	if k >= reflect.Int && k <= reflect.Uint64 {
 | 
			
		||||
		return md.unifyInt(data, rv)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -244,15 +248,14 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
 | 
			
		|||
	case reflect.Bool:
 | 
			
		||||
		return md.unifyBool(data, rv)
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		// we only support empty interfaces.
 | 
			
		||||
		if rv.NumMethod() > 0 {
 | 
			
		||||
			return e("unsupported type %s", rv.Type())
 | 
			
		||||
		if rv.NumMethod() > 0 { // Only support empty interfaces are supported.
 | 
			
		||||
			return md.e("unsupported type %s", rv.Type())
 | 
			
		||||
		}
 | 
			
		||||
		return md.unifyAnything(data, rv)
 | 
			
		||||
	case reflect.Float32, reflect.Float64:
 | 
			
		||||
		return md.unifyFloat64(data, rv)
 | 
			
		||||
	}
 | 
			
		||||
	return e("unsupported type %s", rv.Kind())
 | 
			
		||||
	return md.e("unsupported type %s", rv.Kind())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +264,7 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
 | 
			
		|||
		if mapping == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return e("type mismatch for %s: expected table but found %T",
 | 
			
		||||
		return md.e("type mismatch for %s: expected table but found %T",
 | 
			
		||||
			rv.Type().String(), mapping)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -287,13 +290,14 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
 | 
			
		|||
			if isUnifiable(subv) {
 | 
			
		||||
				md.decoded[md.context.add(key).String()] = struct{}{}
 | 
			
		||||
				md.context = append(md.context, key)
 | 
			
		||||
 | 
			
		||||
				err := md.unify(datum, subv)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				md.context = md.context[0 : len(md.context)-1]
 | 
			
		||||
			} else if f.name != "" {
 | 
			
		||||
				return e("cannot write unexported field %s.%s", rv.Type().String(), f.name)
 | 
			
		||||
				return md.e("cannot write unexported field %s.%s", rv.Type().String(), f.name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -301,10 +305,10 @@ func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
 | 
			
		||||
	if k := rv.Type().Key().Kind(); k != reflect.String {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"toml: cannot decode to a map with non-string key type (%s in %q)",
 | 
			
		||||
			k, rv.Type())
 | 
			
		||||
	keyType := rv.Type().Key().Kind()
 | 
			
		||||
	if keyType != reflect.String && keyType != reflect.Interface {
 | 
			
		||||
		return fmt.Errorf("toml: cannot decode to a map with non-string key type (%s in %q)",
 | 
			
		||||
			keyType, rv.Type())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmap, ok := mapping.(map[string]interface{})
 | 
			
		||||
| 
						 | 
				
			
			@ -322,13 +326,22 @@ func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
 | 
			
		|||
		md.context = append(md.context, k)
 | 
			
		||||
 | 
			
		||||
		rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
 | 
			
		||||
		if err := md.unify(v, rvval); err != nil {
 | 
			
		||||
 | 
			
		||||
		err := md.unify(v, indirect(rvval))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		md.context = md.context[0 : len(md.context)-1]
 | 
			
		||||
 | 
			
		||||
		rvkey := indirect(reflect.New(rv.Type().Key()))
 | 
			
		||||
		rvkey.SetString(k)
 | 
			
		||||
 | 
			
		||||
		switch keyType {
 | 
			
		||||
		case reflect.Interface:
 | 
			
		||||
			rvkey.Set(reflect.ValueOf(k))
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			rvkey.SetString(k)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rv.SetMapIndex(rvkey, rvval)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -343,7 +356,7 @@ func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
 | 
			
		|||
		return md.badtype("slice", data)
 | 
			
		||||
	}
 | 
			
		||||
	if l := datav.Len(); l != rv.Len() {
 | 
			
		||||
		return e("expected array length %d; got TOML array of length %d", rv.Len(), l)
 | 
			
		||||
		return md.e("expected array length %d; got TOML array of length %d", rv.Len(), l)
 | 
			
		||||
	}
 | 
			
		||||
	return md.unifySliceArray(datav, rv)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -376,6 +389,18 @@ func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
 | 
			
		||||
	_, ok := rv.Interface().(json.Number)
 | 
			
		||||
	if ok {
 | 
			
		||||
		if i, ok := data.(int64); ok {
 | 
			
		||||
			rv.SetString(strconv.FormatInt(i, 10))
 | 
			
		||||
		} else if f, ok := data.(float64); ok {
 | 
			
		||||
			rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
 | 
			
		||||
		} else {
 | 
			
		||||
			return md.badtype("string", data)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if s, ok := data.(string); ok {
 | 
			
		||||
		rv.SetString(s)
 | 
			
		||||
		return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -384,11 +409,13 @@ func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
 | 
			
		||||
	rvk := rv.Kind()
 | 
			
		||||
 | 
			
		||||
	if num, ok := data.(float64); ok {
 | 
			
		||||
		switch rv.Kind() {
 | 
			
		||||
		switch rvk {
 | 
			
		||||
		case reflect.Float32:
 | 
			
		||||
			if num < -math.MaxFloat32 || num > math.MaxFloat32 {
 | 
			
		||||
				return e("value %f is out of range for float32", num)
 | 
			
		||||
				return md.parseErr(errParseRange{i: num, size: rvk.String()})
 | 
			
		||||
			}
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case reflect.Float64:
 | 
			
		||||
| 
						 | 
				
			
			@ -400,20 +427,11 @@ func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if num, ok := data.(int64); ok {
 | 
			
		||||
		switch rv.Kind() {
 | 
			
		||||
		case reflect.Float32:
 | 
			
		||||
			if num < -maxSafeFloat32Int || num > maxSafeFloat32Int {
 | 
			
		||||
				return e("value %d is out of range for float32", num)
 | 
			
		||||
			}
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case reflect.Float64:
 | 
			
		||||
			if num < -maxSafeFloat64Int || num > maxSafeFloat64Int {
 | 
			
		||||
				return e("value %d is out of range for float64", num)
 | 
			
		||||
			}
 | 
			
		||||
			rv.SetFloat(float64(num))
 | 
			
		||||
		default:
 | 
			
		||||
			panic("bug")
 | 
			
		||||
		if (rvk == reflect.Float32 && (num < -maxSafeFloat32Int || num > maxSafeFloat32Int)) ||
 | 
			
		||||
			(rvk == reflect.Float64 && (num < -maxSafeFloat64Int || num > maxSafeFloat64Int)) {
 | 
			
		||||
			return md.parseErr(errParseRange{i: num, size: rvk.String()})
 | 
			
		||||
		}
 | 
			
		||||
		rv.SetFloat(float64(num))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -421,50 +439,46 @@ func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
 | 
			
		||||
	if num, ok := data.(int64); ok {
 | 
			
		||||
		if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
 | 
			
		||||
			switch rv.Kind() {
 | 
			
		||||
			case reflect.Int, reflect.Int64:
 | 
			
		||||
				// No bounds checking necessary.
 | 
			
		||||
			case reflect.Int8:
 | 
			
		||||
				if num < math.MinInt8 || num > math.MaxInt8 {
 | 
			
		||||
					return e("value %d is out of range for int8", num)
 | 
			
		||||
				}
 | 
			
		||||
			case reflect.Int16:
 | 
			
		||||
				if num < math.MinInt16 || num > math.MaxInt16 {
 | 
			
		||||
					return e("value %d is out of range for int16", num)
 | 
			
		||||
				}
 | 
			
		||||
			case reflect.Int32:
 | 
			
		||||
				if num < math.MinInt32 || num > math.MaxInt32 {
 | 
			
		||||
					return e("value %d is out of range for int32", num)
 | 
			
		||||
				}
 | 
			
		||||
	_, ok := rv.Interface().(time.Duration)
 | 
			
		||||
	if ok {
 | 
			
		||||
		// Parse as string duration, and fall back to regular integer parsing
 | 
			
		||||
		// (as nanosecond) if this is not a string.
 | 
			
		||||
		if s, ok := data.(string); ok {
 | 
			
		||||
			dur, err := time.ParseDuration(s)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return md.parseErr(errParseDuration{s})
 | 
			
		||||
			}
 | 
			
		||||
			rv.SetInt(num)
 | 
			
		||||
		} else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
 | 
			
		||||
			unum := uint64(num)
 | 
			
		||||
			switch rv.Kind() {
 | 
			
		||||
			case reflect.Uint, reflect.Uint64:
 | 
			
		||||
				// No bounds checking necessary.
 | 
			
		||||
			case reflect.Uint8:
 | 
			
		||||
				if num < 0 || unum > math.MaxUint8 {
 | 
			
		||||
					return e("value %d is out of range for uint8", num)
 | 
			
		||||
				}
 | 
			
		||||
			case reflect.Uint16:
 | 
			
		||||
				if num < 0 || unum > math.MaxUint16 {
 | 
			
		||||
					return e("value %d is out of range for uint16", num)
 | 
			
		||||
				}
 | 
			
		||||
			case reflect.Uint32:
 | 
			
		||||
				if num < 0 || unum > math.MaxUint32 {
 | 
			
		||||
					return e("value %d is out of range for uint32", num)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			rv.SetUint(unum)
 | 
			
		||||
		} else {
 | 
			
		||||
			panic("unreachable")
 | 
			
		||||
			rv.SetInt(int64(dur))
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return md.badtype("integer", data)
 | 
			
		||||
 | 
			
		||||
	num, ok := data.(int64)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return md.badtype("integer", data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rvk := rv.Kind()
 | 
			
		||||
	switch {
 | 
			
		||||
	case rvk >= reflect.Int && rvk <= reflect.Int64:
 | 
			
		||||
		if (rvk == reflect.Int8 && (num < math.MinInt8 || num > math.MaxInt8)) ||
 | 
			
		||||
			(rvk == reflect.Int16 && (num < math.MinInt16 || num > math.MaxInt16)) ||
 | 
			
		||||
			(rvk == reflect.Int32 && (num < math.MinInt32 || num > math.MaxInt32)) {
 | 
			
		||||
			return md.parseErr(errParseRange{i: num, size: rvk.String()})
 | 
			
		||||
		}
 | 
			
		||||
		rv.SetInt(num)
 | 
			
		||||
	case rvk >= reflect.Uint && rvk <= reflect.Uint64:
 | 
			
		||||
		unum := uint64(num)
 | 
			
		||||
		if rvk == reflect.Uint8 && (num < 0 || unum > math.MaxUint8) ||
 | 
			
		||||
			rvk == reflect.Uint16 && (num < 0 || unum > math.MaxUint16) ||
 | 
			
		||||
			rvk == reflect.Uint32 && (num < 0 || unum > math.MaxUint32) {
 | 
			
		||||
			return md.parseErr(errParseRange{i: num, size: rvk.String()})
 | 
			
		||||
		}
 | 
			
		||||
		rv.SetUint(unum)
 | 
			
		||||
	default:
 | 
			
		||||
		panic("unreachable")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
 | 
			
		||||
| 
						 | 
				
			
			@ -489,7 +503,7 @@ func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) erro
 | 
			
		|||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		s = string(text)
 | 
			
		||||
	case TextMarshaler:
 | 
			
		||||
	case encoding.TextMarshaler:
 | 
			
		||||
		text, err := sdata.MarshalText()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
| 
						 | 
				
			
			@ -515,7 +529,30 @@ func (md *MetaData) unifyText(data interface{}, v encoding.TextUnmarshaler) erro
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) badtype(dst string, data interface{}) error {
 | 
			
		||||
	return e("incompatible types: TOML key %q has type %T; destination has type %s", md.context, data, dst)
 | 
			
		||||
	return md.e("incompatible types: TOML value has type %T; destination has type %s", data, dst)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) parseErr(err error) error {
 | 
			
		||||
	k := md.context.String()
 | 
			
		||||
	return ParseError{
 | 
			
		||||
		LastKey:  k,
 | 
			
		||||
		Position: md.keyInfo[k].pos,
 | 
			
		||||
		Line:     md.keyInfo[k].pos.Line,
 | 
			
		||||
		err:      err,
 | 
			
		||||
		input:    string(md.data),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (md *MetaData) e(format string, args ...interface{}) error {
 | 
			
		||||
	f := "toml: "
 | 
			
		||||
	if len(md.context) > 0 {
 | 
			
		||||
		f = fmt.Sprintf("toml: (last key %q): ", md.context)
 | 
			
		||||
		p := md.keyInfo[md.context.String()].pos
 | 
			
		||||
		if p.Line > 0 {
 | 
			
		||||
			f = fmt.Sprintf("toml: line %d (last key %q): ", p.Line, md.context)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf(f+format, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rvalue returns a reflect.Value of `v`. All pointers are resolved.
 | 
			
		||||
| 
						 | 
				
			
			@ -534,7 +571,11 @@ func indirect(v reflect.Value) reflect.Value {
 | 
			
		|||
	if v.Kind() != reflect.Ptr {
 | 
			
		||||
		if v.CanSet() {
 | 
			
		||||
			pv := v.Addr()
 | 
			
		||||
			if _, ok := pv.Interface().(encoding.TextUnmarshaler); ok {
 | 
			
		||||
			pvi := pv.Interface()
 | 
			
		||||
			if _, ok := pvi.(encoding.TextUnmarshaler); ok {
 | 
			
		||||
				return pv
 | 
			
		||||
			}
 | 
			
		||||
			if _, ok := pvi.(Unmarshaler); ok {
 | 
			
		||||
				return pv
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -550,12 +591,12 @@ func isUnifiable(rv reflect.Value) bool {
 | 
			
		|||
	if rv.CanSet() {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
 | 
			
		||||
	rvi := rv.Interface()
 | 
			
		||||
	if _, ok := rvi.(encoding.TextUnmarshaler); ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := rvi.(Unmarshaler); ok {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func e(format string, args ...interface{}) error {
 | 
			
		||||
	return fmt.Errorf("toml: "+format, args...)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package toml
 | 
			
		|||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"encoding"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
| 
						 | 
				
			
			@ -63,6 +64,12 @@ var dblQuotedReplacer = strings.NewReplacer(
 | 
			
		|||
	"\x7f", `\u007f`,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	marshalToml = reflect.TypeOf((*Marshaler)(nil)).Elem()
 | 
			
		||||
	marshalText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
 | 
			
		||||
	timeType    = reflect.TypeOf((*time.Time)(nil)).Elem()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Marshaler is the interface implemented by types that can marshal themselves
 | 
			
		||||
// into valid TOML.
 | 
			
		||||
type Marshaler interface {
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +81,9 @@ type Marshaler interface {
 | 
			
		|||
// The mapping between Go values and TOML values should be precisely the same as
 | 
			
		||||
// for the Decode* functions.
 | 
			
		||||
//
 | 
			
		||||
// time.Time is encoded as a RFC 3339 string, and time.Duration as its string
 | 
			
		||||
// representation.
 | 
			
		||||
//
 | 
			
		||||
// The toml.Marshaler and encoder.TextMarshaler interfaces are supported to
 | 
			
		||||
// encoding the value as custom TOML.
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +95,17 @@ type Marshaler interface {
 | 
			
		|||
//
 | 
			
		||||
// Go maps will be sorted alphabetically by key for deterministic output.
 | 
			
		||||
//
 | 
			
		||||
// The toml struct tag can be used to provide the key name; if omitted the
 | 
			
		||||
// struct field name will be used. If the "omitempty" option is present the
 | 
			
		||||
// following value will be skipped:
 | 
			
		||||
//
 | 
			
		||||
//   - arrays, slices, maps, and string with len of 0
 | 
			
		||||
//   - struct with all zero values
 | 
			
		||||
//   - bool false
 | 
			
		||||
//
 | 
			
		||||
// If omitzero is given all int and float types with a value of 0 will be
 | 
			
		||||
// skipped.
 | 
			
		||||
//
 | 
			
		||||
// Encoding Go values without a corresponding TOML representation will return an
 | 
			
		||||
// error. Examples of this includes maps with non-string keys, slices with nil
 | 
			
		||||
// elements, embedded non-struct types, and nested slices containing maps or
 | 
			
		||||
| 
						 | 
				
			
			@ -136,18 +157,15 @@ func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (enc *Encoder) encode(key Key, rv reflect.Value) {
 | 
			
		||||
	// Special case: time needs to be in ISO8601 format.
 | 
			
		||||
	//
 | 
			
		||||
	// Special case: if we can marshal the type to text, then we used that. This
 | 
			
		||||
	// prevents the encoder for handling these types as generic structs (or
 | 
			
		||||
	// whatever the underlying type of a TextMarshaler is).
 | 
			
		||||
	switch t := rv.Interface().(type) {
 | 
			
		||||
	case time.Time, encoding.TextMarshaler, Marshaler:
 | 
			
		||||
	// If we can marshal the type to text, then we use that. This prevents the
 | 
			
		||||
	// encoder for handling these types as generic structs (or whatever the
 | 
			
		||||
	// underlying type of a TextMarshaler is).
 | 
			
		||||
	switch {
 | 
			
		||||
	case isMarshaler(rv):
 | 
			
		||||
		enc.writeKeyValue(key, rv, false)
 | 
			
		||||
		return
 | 
			
		||||
	// TODO: #76 would make this superfluous after implemented.
 | 
			
		||||
	case Primitive:
 | 
			
		||||
		enc.encode(key, reflect.ValueOf(t.undecoded))
 | 
			
		||||
	case rv.Type() == primitiveType: // TODO: #76 would make this superfluous after implemented.
 | 
			
		||||
		enc.encode(key, reflect.ValueOf(rv.Interface().(Primitive).undecoded))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +230,9 @@ func (enc *Encoder) eElement(rv reflect.Value) {
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			encPanic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if s == nil {
 | 
			
		||||
			encPanic(errors.New("MarshalTOML returned nil and no error"))
 | 
			
		||||
		}
 | 
			
		||||
		enc.w.Write(s)
 | 
			
		||||
		return
 | 
			
		||||
	case encoding.TextMarshaler:
 | 
			
		||||
| 
						 | 
				
			
			@ -219,11 +240,34 @@ func (enc *Encoder) eElement(rv reflect.Value) {
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			encPanic(err)
 | 
			
		||||
		}
 | 
			
		||||
		if s == nil {
 | 
			
		||||
			encPanic(errors.New("MarshalText returned nil and no error"))
 | 
			
		||||
		}
 | 
			
		||||
		enc.writeQuoted(string(s))
 | 
			
		||||
		return
 | 
			
		||||
	case time.Duration:
 | 
			
		||||
		enc.writeQuoted(v.String())
 | 
			
		||||
		return
 | 
			
		||||
	case json.Number:
 | 
			
		||||
		n, _ := rv.Interface().(json.Number)
 | 
			
		||||
 | 
			
		||||
		if n == "" { /// Useful zero value.
 | 
			
		||||
			enc.w.WriteByte('0')
 | 
			
		||||
			return
 | 
			
		||||
		} else if v, err := n.Int64(); err == nil {
 | 
			
		||||
			enc.eElement(reflect.ValueOf(v))
 | 
			
		||||
			return
 | 
			
		||||
		} else if v, err := n.Float64(); err == nil {
 | 
			
		||||
			enc.eElement(reflect.ValueOf(v))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		encPanic(errors.New(fmt.Sprintf("Unable to convert \"%s\" to neither int64 nor float64", n)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch rv.Kind() {
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		enc.eElement(rv.Elem())
 | 
			
		||||
		return
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		enc.writeQuoted(rv.String())
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
| 
						 | 
				
			
			@ -259,7 +303,7 @@ func (enc *Encoder) eElement(rv reflect.Value) {
 | 
			
		|||
	case reflect.Interface:
 | 
			
		||||
		enc.eElement(rv.Elem())
 | 
			
		||||
	default:
 | 
			
		||||
		encPanic(fmt.Errorf("unexpected primitive type: %T", rv.Interface()))
 | 
			
		||||
		encPanic(fmt.Errorf("unexpected type: %T", rv.Interface()))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +324,7 @@ func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
 | 
			
		|||
	length := rv.Len()
 | 
			
		||||
	enc.wf("[")
 | 
			
		||||
	for i := 0; i < length; i++ {
 | 
			
		||||
		elem := rv.Index(i)
 | 
			
		||||
		elem := eindirect(rv.Index(i))
 | 
			
		||||
		enc.eElement(elem)
 | 
			
		||||
		if i != length-1 {
 | 
			
		||||
			enc.wf(", ")
 | 
			
		||||
| 
						 | 
				
			
			@ -294,7 +338,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
 | 
			
		|||
		encPanic(errNoKey)
 | 
			
		||||
	}
 | 
			
		||||
	for i := 0; i < rv.Len(); i++ {
 | 
			
		||||
		trv := rv.Index(i)
 | 
			
		||||
		trv := eindirect(rv.Index(i))
 | 
			
		||||
		if isNil(trv) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +363,7 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value, inline bool) {
 | 
			
		||||
	switch rv := eindirect(rv); rv.Kind() {
 | 
			
		||||
	switch rv.Kind() {
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		enc.eMap(key, rv, inline)
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +385,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
 | 
			
		|||
	var mapKeysDirect, mapKeysSub []string
 | 
			
		||||
	for _, mapKey := range rv.MapKeys() {
 | 
			
		||||
		k := mapKey.String()
 | 
			
		||||
		if typeIsTable(tomlTypeOfGo(rv.MapIndex(mapKey))) {
 | 
			
		||||
		if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
 | 
			
		||||
			mapKeysSub = append(mapKeysSub, k)
 | 
			
		||||
		} else {
 | 
			
		||||
			mapKeysDirect = append(mapKeysDirect, k)
 | 
			
		||||
| 
						 | 
				
			
			@ -351,7 +395,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
 | 
			
		|||
	var writeMapKeys = func(mapKeys []string, trailC bool) {
 | 
			
		||||
		sort.Strings(mapKeys)
 | 
			
		||||
		for i, mapKey := range mapKeys {
 | 
			
		||||
			val := rv.MapIndex(reflect.ValueOf(mapKey))
 | 
			
		||||
			val := eindirect(rv.MapIndex(reflect.ValueOf(mapKey)))
 | 
			
		||||
			if isNil(val) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -379,6 +423,13 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
 | 
			
		|||
 | 
			
		||||
const is32Bit = (32 << (^uint(0) >> 63)) == 32
 | 
			
		||||
 | 
			
		||||
func pointerTo(t reflect.Type) reflect.Type {
 | 
			
		||||
	if t.Kind() == reflect.Ptr {
 | 
			
		||||
		return pointerTo(t.Elem())
 | 
			
		||||
	}
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
 | 
			
		||||
	// Write keys for fields directly under this key first, because if we write
 | 
			
		||||
	// a field that creates a new table then all keys under it will be in that
 | 
			
		||||
| 
						 | 
				
			
			@ -395,7 +446,8 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
 | 
			
		|||
	addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
 | 
			
		||||
		for i := 0; i < rt.NumField(); i++ {
 | 
			
		||||
			f := rt.Field(i)
 | 
			
		||||
			if f.PkgPath != "" && !f.Anonymous { /// Skip unexported fields.
 | 
			
		||||
			isEmbed := f.Anonymous && pointerTo(f.Type).Kind() == reflect.Struct
 | 
			
		||||
			if f.PkgPath != "" && !isEmbed { /// Skip unexported fields.
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			opts := getOptions(f.Tag)
 | 
			
		||||
| 
						 | 
				
			
			@ -403,27 +455,16 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
 | 
			
		|||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			frv := rv.Field(i)
 | 
			
		||||
			frv := eindirect(rv.Field(i))
 | 
			
		||||
 | 
			
		||||
			// Treat anonymous struct fields with tag names as though they are
 | 
			
		||||
			// not anonymous, like encoding/json does.
 | 
			
		||||
			//
 | 
			
		||||
			// Non-struct anonymous fields use the normal encoding logic.
 | 
			
		||||
			if f.Anonymous {
 | 
			
		||||
				t := f.Type
 | 
			
		||||
				switch t.Kind() {
 | 
			
		||||
				case reflect.Struct:
 | 
			
		||||
					if getOptions(f.Tag).name == "" {
 | 
			
		||||
						addFields(t, frv, append(start, f.Index...))
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				case reflect.Ptr:
 | 
			
		||||
					if t.Elem().Kind() == reflect.Struct && getOptions(f.Tag).name == "" {
 | 
			
		||||
						if !frv.IsNil() {
 | 
			
		||||
							addFields(t.Elem(), frv.Elem(), append(start, f.Index...))
 | 
			
		||||
						}
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
			if isEmbed {
 | 
			
		||||
				if getOptions(f.Tag).name == "" && frv.Kind() == reflect.Struct {
 | 
			
		||||
					addFields(frv.Type(), frv, append(start, f.Index...))
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -449,7 +490,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
 | 
			
		|||
	writeFields := func(fields [][]int) {
 | 
			
		||||
		for _, fieldIndex := range fields {
 | 
			
		||||
			fieldType := rt.FieldByIndex(fieldIndex)
 | 
			
		||||
			fieldVal := rv.FieldByIndex(fieldIndex)
 | 
			
		||||
			fieldVal := eindirect(rv.FieldByIndex(fieldIndex))
 | 
			
		||||
 | 
			
		||||
			if isNil(fieldVal) { /// Don't write anything for nil fields.
 | 
			
		||||
				continue
 | 
			
		||||
| 
						 | 
				
			
			@ -502,6 +543,21 @@ func tomlTypeOfGo(rv reflect.Value) tomlType {
 | 
			
		|||
	if isNil(rv) || !rv.IsValid() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if rv.Kind() == reflect.Struct {
 | 
			
		||||
		if rv.Type() == timeType {
 | 
			
		||||
			return tomlDatetime
 | 
			
		||||
		}
 | 
			
		||||
		if isMarshaler(rv) {
 | 
			
		||||
			return tomlString
 | 
			
		||||
		}
 | 
			
		||||
		return tomlHash
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isMarshaler(rv) {
 | 
			
		||||
		return tomlString
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch rv.Kind() {
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		return tomlBool
 | 
			
		||||
| 
						 | 
				
			
			@ -513,7 +569,7 @@ func tomlTypeOfGo(rv reflect.Value) tomlType {
 | 
			
		|||
	case reflect.Float32, reflect.Float64:
 | 
			
		||||
		return tomlFloat
 | 
			
		||||
	case reflect.Array, reflect.Slice:
 | 
			
		||||
		if typeEqual(tomlHash, tomlArrayType(rv)) {
 | 
			
		||||
		if isTableArray(rv) {
 | 
			
		||||
			return tomlArrayHash
 | 
			
		||||
		}
 | 
			
		||||
		return tomlArray
 | 
			
		||||
| 
						 | 
				
			
			@ -523,67 +579,35 @@ func tomlTypeOfGo(rv reflect.Value) tomlType {
 | 
			
		|||
		return tomlString
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		return tomlHash
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		if _, ok := rv.Interface().(time.Time); ok {
 | 
			
		||||
			return tomlDatetime
 | 
			
		||||
		}
 | 
			
		||||
		if isMarshaler(rv) {
 | 
			
		||||
			return tomlString
 | 
			
		||||
		}
 | 
			
		||||
		return tomlHash
 | 
			
		||||
	default:
 | 
			
		||||
		if isMarshaler(rv) {
 | 
			
		||||
			return tomlString
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		encPanic(errors.New("unsupported type: " + rv.Kind().String()))
 | 
			
		||||
		panic("unreachable")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isMarshaler(rv reflect.Value) bool {
 | 
			
		||||
	switch rv.Interface().(type) {
 | 
			
		||||
	case encoding.TextMarshaler:
 | 
			
		||||
		return true
 | 
			
		||||
	case Marshaler:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Someone used a pointer receiver: we can make it work for pointer values.
 | 
			
		||||
	if rv.CanAddr() {
 | 
			
		||||
		if _, ok := rv.Addr().Interface().(encoding.TextMarshaler); ok {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if _, ok := rv.Addr().Interface().(Marshaler); ok {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
	return rv.Type().Implements(marshalText) || rv.Type().Implements(marshalToml)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tomlArrayType returns the element type of a TOML array. The type returned
 | 
			
		||||
// may be nil if it cannot be determined (e.g., a nil slice or a zero length
 | 
			
		||||
// slize). This function may also panic if it finds a type that cannot be
 | 
			
		||||
// expressed in TOML (such as nil elements, heterogeneous arrays or directly
 | 
			
		||||
// nested arrays of tables).
 | 
			
		||||
func tomlArrayType(rv reflect.Value) tomlType {
 | 
			
		||||
	if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
// isTableArray reports if all entries in the array or slice are a table.
 | 
			
		||||
func isTableArray(arr reflect.Value) bool {
 | 
			
		||||
	if isNil(arr) || !arr.IsValid() || arr.Len() == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Don't allow nil.
 | 
			
		||||
	rvlen := rv.Len()
 | 
			
		||||
	for i := 1; i < rvlen; i++ {
 | 
			
		||||
		if tomlTypeOfGo(rv.Index(i)) == nil {
 | 
			
		||||
	ret := true
 | 
			
		||||
	for i := 0; i < arr.Len(); i++ {
 | 
			
		||||
		tt := tomlTypeOfGo(eindirect(arr.Index(i)))
 | 
			
		||||
		// Don't allow nil.
 | 
			
		||||
		if tt == nil {
 | 
			
		||||
			encPanic(errArrayNilElement)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	firstType := tomlTypeOfGo(rv.Index(0))
 | 
			
		||||
	if firstType == nil {
 | 
			
		||||
		encPanic(errArrayNilElement)
 | 
			
		||||
		if ret && !typeEqual(tomlHash, tt) {
 | 
			
		||||
			ret = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return firstType
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tagOptions struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -628,6 +652,8 @@ func isEmpty(rv reflect.Value) bool {
 | 
			
		|||
	switch rv.Kind() {
 | 
			
		||||
	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
 | 
			
		||||
		return rv.Len() == 0
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		return reflect.Zero(rv.Type()).Interface() == rv.Interface()
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		return !rv.Bool()
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -679,13 +705,25 @@ func encPanic(err error) {
 | 
			
		|||
	panic(tomlEncodeError{err})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Resolve any level of pointers to the actual value (e.g. **string → string).
 | 
			
		||||
func eindirect(v reflect.Value) reflect.Value {
 | 
			
		||||
	switch v.Kind() {
 | 
			
		||||
	case reflect.Ptr, reflect.Interface:
 | 
			
		||||
		return eindirect(v.Elem())
 | 
			
		||||
	default:
 | 
			
		||||
	if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
 | 
			
		||||
		if isMarshaler(v) {
 | 
			
		||||
			return v
 | 
			
		||||
		}
 | 
			
		||||
		if v.CanAddr() { /// Special case for marshalers; see #358.
 | 
			
		||||
			if pv := v.Addr(); isMarshaler(pv) {
 | 
			
		||||
				return pv
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.IsNil() {
 | 
			
		||||
		return v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return eindirect(v.Elem())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isNil(rv reflect.Value) bool {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -128,9 +128,13 @@ func (pe ParseError) ErrorWithPosition() string {
 | 
			
		|||
func (pe ParseError) ErrorWithUsage() string {
 | 
			
		||||
	m := pe.ErrorWithPosition()
 | 
			
		||||
	if u, ok := pe.err.(interface{ Usage() string }); ok && u.Usage() != "" {
 | 
			
		||||
		return m + "Error help:\n\n    " +
 | 
			
		||||
			strings.ReplaceAll(strings.TrimSpace(u.Usage()), "\n", "\n    ") +
 | 
			
		||||
			"\n"
 | 
			
		||||
		lines := strings.Split(strings.TrimSpace(u.Usage()), "\n")
 | 
			
		||||
		for i := range lines {
 | 
			
		||||
			if lines[i] != "" {
 | 
			
		||||
				lines[i] = "    " + lines[i]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return m + "Error help:\n\n" + strings.Join(lines, "\n") + "\n"
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +164,11 @@ type (
 | 
			
		|||
	errLexInvalidDate   struct{ v string }
 | 
			
		||||
	errLexInlineTableNL struct{}
 | 
			
		||||
	errLexStringNL      struct{}
 | 
			
		||||
	errParseRange       struct {
 | 
			
		||||
		i    interface{} // int or float
 | 
			
		||||
		size string      // "int64", "uint16", etc.
 | 
			
		||||
	}
 | 
			
		||||
	errParseDuration struct{ d string }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (e errLexControl) Error() string {
 | 
			
		||||
| 
						 | 
				
			
			@ -179,6 +188,10 @@ func (e errLexInlineTableNL) Error() string { return "newlines not allowed withi
 | 
			
		|||
func (e errLexInlineTableNL) Usage() string { return usageInlineNewline }
 | 
			
		||||
func (e errLexStringNL) Error() string      { return "strings cannot contain newlines" }
 | 
			
		||||
func (e errLexStringNL) Usage() string      { return usageStringNewline }
 | 
			
		||||
func (e errParseRange) Error() string       { return fmt.Sprintf("%v is out of range for %s", e.i, e.size) }
 | 
			
		||||
func (e errParseRange) Usage() string       { return usageIntOverflow }
 | 
			
		||||
func (e errParseDuration) Error() string    { return fmt.Sprintf("invalid duration: %q", e.d) }
 | 
			
		||||
func (e errParseDuration) Usage() string    { return usageDuration }
 | 
			
		||||
 | 
			
		||||
const usageEscape = `
 | 
			
		||||
A '\' inside a "-delimited string is interpreted as an escape character.
 | 
			
		||||
| 
						 | 
				
			
			@ -227,3 +240,37 @@ Instead use """ or ''' to split strings over multiple lines:
 | 
			
		|||
    string = """Hello,
 | 
			
		||||
    world!"""
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const usageIntOverflow = `
 | 
			
		||||
This number is too large; this may be an error in the TOML, but it can also be a
 | 
			
		||||
bug in the program that uses too small of an integer.
 | 
			
		||||
 | 
			
		||||
The maximum and minimum values are:
 | 
			
		||||
 | 
			
		||||
    size   │ lowest         │ highest
 | 
			
		||||
    ───────┼────────────────┼──────────
 | 
			
		||||
    int8   │ -128           │ 127
 | 
			
		||||
    int16  │ -32,768        │ 32,767
 | 
			
		||||
    int32  │ -2,147,483,648 │ 2,147,483,647
 | 
			
		||||
    int64  │ -9.2 × 10¹⁷    │ 9.2 × 10¹⁷
 | 
			
		||||
    uint8  │ 0              │ 255
 | 
			
		||||
    uint16 │ 0              │ 65535
 | 
			
		||||
    uint32 │ 0              │ 4294967295
 | 
			
		||||
    uint64 │ 0              │ 1.8 × 10¹⁸
 | 
			
		||||
 | 
			
		||||
int refers to int32 on 32-bit systems and int64 on 64-bit systems.
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const usageDuration = `
 | 
			
		||||
A duration must be as "number<unit>", without any spaces. Valid units are:
 | 
			
		||||
 | 
			
		||||
    ns         nanoseconds (billionth of a second)
 | 
			
		||||
    us, µs     microseconds (millionth of a second)
 | 
			
		||||
    ms         milliseconds (thousands of a second)
 | 
			
		||||
    s          seconds
 | 
			
		||||
    m          minutes
 | 
			
		||||
    h          hours
 | 
			
		||||
 | 
			
		||||
You can combine multiple units; for example "5m10s" for 5 minutes and 10
 | 
			
		||||
seconds.
 | 
			
		||||
`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ func (lx *lexer) nextItem() item {
 | 
			
		|||
			return item
 | 
			
		||||
		default:
 | 
			
		||||
			lx.state = lx.state(lx)
 | 
			
		||||
			//fmt.Printf("     STATE %-24s  current: %-10q	stack: %s\n", lx.state, lx.current(), lx.stack)
 | 
			
		||||
			//fmt.Printf("     STATE %-24s  current: %-10s	stack: %s\n", lx.state, lx.current(), lx.stack)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -716,7 +716,17 @@ func lexMultilineString(lx *lexer) stateFn {
 | 
			
		|||
				if lx.peek() == '"' {
 | 
			
		||||
					/// Check if we already lexed 5 's; if so we have 6 now, and
 | 
			
		||||
					/// that's just too many man!
 | 
			
		||||
					if strings.HasSuffix(lx.current(), `"""""`) {
 | 
			
		||||
					///
 | 
			
		||||
					/// Second check is for the edge case:
 | 
			
		||||
					///
 | 
			
		||||
					///            two quotes allowed.
 | 
			
		||||
					///            vv
 | 
			
		||||
					///   """lol \""""""
 | 
			
		||||
					///          ^^  ^^^---- closing three
 | 
			
		||||
					///     escaped
 | 
			
		||||
					///
 | 
			
		||||
					/// But ugly, but it works
 | 
			
		||||
					if strings.HasSuffix(lx.current(), `"""""`) && !strings.HasSuffix(lx.current(), `\"""""`) {
 | 
			
		||||
						return lx.errorf(`unexpected '""""""'`)
 | 
			
		||||
					}
 | 
			
		||||
					lx.backup()
 | 
			
		||||
| 
						 | 
				
			
			@ -807,8 +817,7 @@ func lexMultilineRawString(lx *lexer) stateFn {
 | 
			
		|||
// lexMultilineStringEscape consumes an escaped character. It assumes that the
 | 
			
		||||
// preceding '\\' has already been consumed.
 | 
			
		||||
func lexMultilineStringEscape(lx *lexer) stateFn {
 | 
			
		||||
	// Handle the special case first:
 | 
			
		||||
	if isNL(lx.next()) {
 | 
			
		||||
	if isNL(lx.next()) { /// \ escaping newline.
 | 
			
		||||
		return lexMultilineString
 | 
			
		||||
	}
 | 
			
		||||
	lx.backup()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,10 +12,11 @@ import (
 | 
			
		|||
type MetaData struct {
 | 
			
		||||
	context Key // Used only during decoding.
 | 
			
		||||
 | 
			
		||||
	keyInfo map[string]keyInfo
 | 
			
		||||
	mapping map[string]interface{}
 | 
			
		||||
	types   map[string]tomlType
 | 
			
		||||
	keys    []Key
 | 
			
		||||
	decoded map[string]struct{}
 | 
			
		||||
	data    []byte // Input file; for errors.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsDefined reports if the key exists in the TOML data.
 | 
			
		||||
| 
						 | 
				
			
			@ -50,8 +51,8 @@ func (md *MetaData) IsDefined(key ...string) bool {
 | 
			
		|||
// Type will return the empty string if given an empty key or a key that does
 | 
			
		||||
// not exist. Keys are case sensitive.
 | 
			
		||||
func (md *MetaData) Type(key ...string) string {
 | 
			
		||||
	if typ, ok := md.types[Key(key).String()]; ok {
 | 
			
		||||
		return typ.typeString()
 | 
			
		||||
	if ki, ok := md.keyInfo[Key(key).String()]; ok {
 | 
			
		||||
		return ki.tomlType.typeString()
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,12 +16,18 @@ type parser struct {
 | 
			
		|||
	currentKey string   // Base key name for everything except hashes.
 | 
			
		||||
	pos        Position // Current position in the TOML file.
 | 
			
		||||
 | 
			
		||||
	ordered   []Key                  // List of keys in the order that they appear in the TOML data.
 | 
			
		||||
	ordered []Key // List of keys in the order that they appear in the TOML data.
 | 
			
		||||
 | 
			
		||||
	keyInfo   map[string]keyInfo     // Map keyname → info about the TOML key.
 | 
			
		||||
	mapping   map[string]interface{} // Map keyname → key value.
 | 
			
		||||
	types     map[string]tomlType    // Map keyname → TOML type.
 | 
			
		||||
	implicits map[string]struct{}    // Record implicit keys (e.g. "key.group.names").
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type keyInfo struct {
 | 
			
		||||
	pos      Position
 | 
			
		||||
	tomlType tomlType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parse(data string) (p *parser, err error) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -57,8 +63,8 @@ func parse(data string) (p *parser, err error) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	p = &parser{
 | 
			
		||||
		keyInfo:   make(map[string]keyInfo),
 | 
			
		||||
		mapping:   make(map[string]interface{}),
 | 
			
		||||
		types:     make(map[string]tomlType),
 | 
			
		||||
		lx:        lex(data),
 | 
			
		||||
		ordered:   make([]Key, 0),
 | 
			
		||||
		implicits: make(map[string]struct{}),
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +80,15 @@ func parse(data string) (p *parser, err error) {
 | 
			
		|||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) panicErr(it item, err error) {
 | 
			
		||||
	panic(ParseError{
 | 
			
		||||
		err:      err,
 | 
			
		||||
		Position: it.pos,
 | 
			
		||||
		Line:     it.pos.Len,
 | 
			
		||||
		LastKey:  p.current(),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) panicItemf(it item, format string, v ...interface{}) {
 | 
			
		||||
	panic(ParseError{
 | 
			
		||||
		Message:  fmt.Sprintf(format, v...),
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +109,7 @@ func (p *parser) panicf(format string, v ...interface{}) {
 | 
			
		|||
 | 
			
		||||
func (p *parser) next() item {
 | 
			
		||||
	it := p.lx.nextItem()
 | 
			
		||||
	//fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.line, it.val)
 | 
			
		||||
	//fmt.Printf("ITEM %-18s line %-3d │ %q\n", it.typ, it.pos.Line, it.val)
 | 
			
		||||
	if it.typ == itemError {
 | 
			
		||||
		if it.err != nil {
 | 
			
		||||
			panic(ParseError{
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +161,7 @@ func (p *parser) topLevel(item item) {
 | 
			
		|||
		p.assertEqual(itemTableEnd, name.typ)
 | 
			
		||||
 | 
			
		||||
		p.addContext(key, false)
 | 
			
		||||
		p.setType("", tomlHash)
 | 
			
		||||
		p.setType("", tomlHash, item.pos)
 | 
			
		||||
		p.ordered = append(p.ordered, key)
 | 
			
		||||
	case itemArrayTableStart: // [[ .. ]]
 | 
			
		||||
		name := p.nextPos()
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +173,7 @@ func (p *parser) topLevel(item item) {
 | 
			
		|||
		p.assertEqual(itemArrayTableEnd, name.typ)
 | 
			
		||||
 | 
			
		||||
		p.addContext(key, true)
 | 
			
		||||
		p.setType("", tomlArrayHash)
 | 
			
		||||
		p.setType("", tomlArrayHash, item.pos)
 | 
			
		||||
		p.ordered = append(p.ordered, key)
 | 
			
		||||
	case itemKeyStart: // key = ..
 | 
			
		||||
		outerContext := p.context
 | 
			
		||||
| 
						 | 
				
			
			@ -181,8 +196,9 @@ func (p *parser) topLevel(item item) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		/// Set value.
 | 
			
		||||
		val, typ := p.value(p.next(), false)
 | 
			
		||||
		p.set(p.currentKey, val, typ)
 | 
			
		||||
		vItem := p.next()
 | 
			
		||||
		val, typ := p.value(vItem, false)
 | 
			
		||||
		p.set(p.currentKey, val, typ, vItem.pos)
 | 
			
		||||
		p.ordered = append(p.ordered, p.context.add(p.currentKey))
 | 
			
		||||
 | 
			
		||||
		/// Remove the context we added (preserving any context from [tbl] lines).
 | 
			
		||||
| 
						 | 
				
			
			@ -266,7 +282,7 @@ func (p *parser) valueInteger(it item) (interface{}, tomlType) {
 | 
			
		|||
		// So mark the former as a bug but the latter as a legitimate user
 | 
			
		||||
		// error.
 | 
			
		||||
		if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
 | 
			
		||||
			p.panicItemf(it, "Integer '%s' is out of the range of 64-bit signed integers.", it.val)
 | 
			
		||||
			p.panicErr(it, errParseRange{i: it.val, size: "int64"})
 | 
			
		||||
		} else {
 | 
			
		||||
			p.bug("Expected integer value, but got '%s'.", it.val)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +320,7 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) {
 | 
			
		|||
	num, err := strconv.ParseFloat(val, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
 | 
			
		||||
			p.panicItemf(it, "Float '%s' is out of the range of 64-bit IEEE-754 floating-point numbers.", it.val)
 | 
			
		||||
			p.panicErr(it, errParseRange{i: it.val, size: "float64"})
 | 
			
		||||
		} else {
 | 
			
		||||
			p.panicItemf(it, "Invalid float value: %q", it.val)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -343,9 +359,8 @@ func (p *parser) valueDatetime(it item) (interface{}, tomlType) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (p *parser) valueArray(it item) (interface{}, tomlType) {
 | 
			
		||||
	p.setType(p.currentKey, tomlArray)
 | 
			
		||||
	p.setType(p.currentKey, tomlArray, it.pos)
 | 
			
		||||
 | 
			
		||||
	// p.setType(p.currentKey, typ)
 | 
			
		||||
	var (
 | 
			
		||||
		types []tomlType
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -414,7 +429,7 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom
 | 
			
		|||
 | 
			
		||||
		/// Set the value.
 | 
			
		||||
		val, typ := p.value(p.next(), false)
 | 
			
		||||
		p.set(p.currentKey, val, typ)
 | 
			
		||||
		p.set(p.currentKey, val, typ, it.pos)
 | 
			
		||||
		p.ordered = append(p.ordered, p.context.add(p.currentKey))
 | 
			
		||||
		hash[p.currentKey] = val
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -533,9 +548,10 @@ func (p *parser) addContext(key Key, array bool) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// set calls setValue and setType.
 | 
			
		||||
func (p *parser) set(key string, val interface{}, typ tomlType) {
 | 
			
		||||
func (p *parser) set(key string, val interface{}, typ tomlType, pos Position) {
 | 
			
		||||
	p.setValue(key, val)
 | 
			
		||||
	p.setType(key, typ)
 | 
			
		||||
	p.setType(key, typ, pos)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// setValue sets the given key to the given value in the current context.
 | 
			
		||||
| 
						 | 
				
			
			@ -599,7 +615,7 @@ func (p *parser) setValue(key string, value interface{}) {
 | 
			
		|||
//
 | 
			
		||||
// Note that if `key` is empty, then the type given will be applied to the
 | 
			
		||||
// current context (which is either a table or an array of tables).
 | 
			
		||||
func (p *parser) setType(key string, typ tomlType) {
 | 
			
		||||
func (p *parser) setType(key string, typ tomlType, pos Position) {
 | 
			
		||||
	keyContext := make(Key, 0, len(p.context)+1)
 | 
			
		||||
	keyContext = append(keyContext, p.context...)
 | 
			
		||||
	if len(key) > 0 { // allow type setting for hashes
 | 
			
		||||
| 
						 | 
				
			
			@ -611,7 +627,7 @@ func (p *parser) setType(key string, typ tomlType) {
 | 
			
		|||
	if len(keyContext) == 0 {
 | 
			
		||||
		keyContext = Key{""}
 | 
			
		||||
	}
 | 
			
		||||
	p.types[keyContext.String()] = typ
 | 
			
		||||
	p.keyInfo[keyContext.String()] = keyInfo{tomlType: typ, pos: pos}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and
 | 
			
		||||
| 
						 | 
				
			
			@ -619,7 +635,7 @@ func (p *parser) setType(key string, typ tomlType) {
 | 
			
		|||
func (p *parser) addImplicit(key Key)     { p.implicits[key.String()] = struct{}{} }
 | 
			
		||||
func (p *parser) removeImplicit(key Key)  { delete(p.implicits, key.String()) }
 | 
			
		||||
func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok }
 | 
			
		||||
func (p *parser) isArray(key Key) bool    { return p.types[key.String()] == tomlArray }
 | 
			
		||||
func (p *parser) isArray(key Key) bool    { return p.keyInfo[key.String()].tomlType == tomlArray }
 | 
			
		||||
func (p *parser) addImplicitContext(key Key) {
 | 
			
		||||
	p.addImplicit(key)
 | 
			
		||||
	p.addContext(key, false)
 | 
			
		||||
| 
						 | 
				
			
			@ -710,10 +726,8 @@ func (p *parser) replaceEscapes(it item, str string) string {
 | 
			
		|||
		switch s[r] {
 | 
			
		||||
		default:
 | 
			
		||||
			p.bug("Expected valid escape code after \\, but got %q.", s[r])
 | 
			
		||||
			return ""
 | 
			
		||||
		case ' ', '\t':
 | 
			
		||||
			p.panicItemf(it, "invalid escape: '\\%c'", s[r])
 | 
			
		||||
			return ""
 | 
			
		||||
		case 'b':
 | 
			
		||||
			replaced = append(replaced, rune(0x0008))
 | 
			
		||||
			r += 1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
# github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1
 | 
			
		||||
github.com/Azure/go-ansiterm
 | 
			
		||||
github.com/Azure/go-ansiterm/winterm
 | 
			
		||||
# github.com/BurntSushi/toml v1.1.0
 | 
			
		||||
# github.com/BurntSushi/toml v1.2.0
 | 
			
		||||
## explicit
 | 
			
		||||
github.com/BurntSushi/toml
 | 
			
		||||
github.com/BurntSushi/toml/internal
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue