mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	libcontainerd/supervisor: replace BurntSushi/toml with pelletier/go-toml
Taking the same approach as was taken in containerd
The new library has a slightly different output;
- keys at the same level are sorted alphabetically
- empty sections not omitted (`proxy_plugins`, `stream_processors`, `timeouts`),
  which could possibly be be addressed with an "omitempty" in containerd's struct.
- empty slices are not omitted (`imports`, `required_plugins`)
After sorting the "before" configuration the diff looks like this:
```patch
diff --git a/config-before-sorted.toml b/config-after.toml
index cc771ce7ab..43a727f589 100644
--- a/config-before-sorted.toml
+++ b/config-after.toml
@@ -1,6 +1,8 @@
 disabled_plugins = ["cri"]
+imports = []
 oom_score = 0
 plugin_dir = ""
+required_plugins = []
 root = "/var/lib/docker/containerd/daemon"
 state = "/var/run/docker/containerd/daemon"
 version = 0
@@ -37,6 +39,12 @@ version = 0
     shim = "containerd-shim"
     shim_debug = true
+[proxy_plugins]
+
+[stream_processors]
+
+[timeouts]
+
 [ttrpc]
   address = ""
   gid = 0
```
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
			
			
This commit is contained in:
		
							parent
							
								
									1d4a273487
								
							
						
					
					
						commit
						2a7c1cc1d6
					
				
					 19 changed files with 4569 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -13,10 +13,10 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	"github.com/containerd/containerd"
 | 
			
		||||
	"github.com/containerd/containerd/services/server/config"
 | 
			
		||||
	"github.com/docker/docker/pkg/system"
 | 
			
		||||
	"github.com/pelletier/go-toml"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -31,13 +31,11 @@ const (
 | 
			
		|||
	pidFile                 = "containerd.pid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type pluginConfigs struct {
 | 
			
		||||
	Plugins map[string]interface{} `toml:"plugins"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type remote struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	config.Config
 | 
			
		||||
	// Plugins overrides `Plugins map[string]toml.Tree` in config config.
 | 
			
		||||
	Plugins map[string]interface{} `toml:"plugins"`
 | 
			
		||||
 | 
			
		||||
	daemonPid int
 | 
			
		||||
	logger    *logrus.Entry
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +44,8 @@ type remote struct {
 | 
			
		|||
	daemonStartCh chan error
 | 
			
		||||
	daemonStopCh  chan struct{}
 | 
			
		||||
 | 
			
		||||
	rootDir     string
 | 
			
		||||
	stateDir    string
 | 
			
		||||
	pluginConfs pluginConfigs
 | 
			
		||||
	rootDir  string
 | 
			
		||||
	stateDir string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Daemon represents a running containerd daemon
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +66,7 @@ func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Da
 | 
			
		|||
			Root:  filepath.Join(rootDir, "daemon"),
 | 
			
		||||
			State: filepath.Join(stateDir, "daemon"),
 | 
			
		||||
		},
 | 
			
		||||
		pluginConfs:   pluginConfigs{make(map[string]interface{})},
 | 
			
		||||
		Plugins:       make(map[string]interface{}),
 | 
			
		||||
		daemonPid:     -1,
 | 
			
		||||
		logger:        logrus.WithField("module", "libcontainerd"),
 | 
			
		||||
		daemonStartCh: make(chan error, 1),
 | 
			
		||||
| 
						 | 
				
			
			@ -157,14 +154,9 @@ func (r *remote) getContainerdConfig() (string, error) {
 | 
			
		|||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	enc := toml.NewEncoder(f)
 | 
			
		||||
	if err = enc.Encode(r.Config); err != nil {
 | 
			
		||||
		return "", errors.Wrapf(err, "failed to encode general config")
 | 
			
		||||
	if err := toml.NewEncoder(f).Encode(r); err != nil {
 | 
			
		||||
		return "", errors.Wrapf(err, "failed to write containerd config file (%s)", path)
 | 
			
		||||
	}
 | 
			
		||||
	if err = enc.Encode(r.pluginConfs); err != nil {
 | 
			
		||||
		return "", errors.Wrapf(err, "failed to encode plugin configs")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return path, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,10 +29,10 @@ func (r *remote) setDefaults() {
 | 
			
		|||
		r.Debug.Address = filepath.Join(r.stateDir, debugSockFile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for key, conf := range r.pluginConfs.Plugins {
 | 
			
		||||
	for key, conf := range r.Plugins {
 | 
			
		||||
		if conf == nil {
 | 
			
		||||
			r.DisabledPlugins = append(r.DisabledPlugins, key)
 | 
			
		||||
			delete(r.pluginConfs.Plugins, key)
 | 
			
		||||
			delete(r.Plugins, key)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ func WithMetricsAddress(addr string) DaemonOpt {
 | 
			
		|||
// the toml format.
 | 
			
		||||
func WithPlugin(name string, conf interface{}) DaemonOpt {
 | 
			
		||||
	return func(r *remote) error {
 | 
			
		||||
		r.pluginConfs.Plugins[name] = conf
 | 
			
		||||
		r.Plugins[name] = conf
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,6 +139,7 @@ github.com/containerd/typeurl                       cd3ce7159eae562a4f60ceff37da
 | 
			
		|||
github.com/containerd/ttrpc                         bfba540dc45464586c106b1f31c8547933c1eb41 # v1.0.2
 | 
			
		||||
github.com/gogo/googleapis                          01e0f9cca9b92166042241267ee2a5cdf5cff46c # v1.3.2
 | 
			
		||||
github.com/cilium/ebpf                              1c8d4c9ef7759622653a1d319284a44652333b28
 | 
			
		||||
github.com/pelletier/go-toml                        65ca8064882c8c308e5c804c5d5443d409e0738c # v1.8.1
 | 
			
		||||
 | 
			
		||||
# cluster
 | 
			
		||||
github.com/docker/swarmkit                          17d8d4e4d8bdec33d386e6362d3537fa9493ba00
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								vendor/github.com/pelletier/go-toml/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/pelletier/go-toml/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2013 - 2017 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
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										151
									
								
								vendor/github.com/pelletier/go-toml/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vendor/github.com/pelletier/go-toml/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,151 @@
 | 
			
		|||
# go-toml
 | 
			
		||||
 | 
			
		||||
Go library for the [TOML](https://github.com/mojombo/toml) format.
 | 
			
		||||
 | 
			
		||||
This library supports TOML version
 | 
			
		||||
[v1.0.0-rc.1](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v1.0.0-rc.1.md)
 | 
			
		||||
 | 
			
		||||
[](http://godoc.org/github.com/pelletier/go-toml)
 | 
			
		||||
[](https://github.com/pelletier/go-toml/blob/master/LICENSE)
 | 
			
		||||
[](https://dev.azure.com/pelletierthomas/go-toml-ci/_build/latest?definitionId=1&branchName=master)
 | 
			
		||||
[](https://codecov.io/gh/pelletier/go-toml)
 | 
			
		||||
[](https://goreportcard.com/report/github.com/pelletier/go-toml)
 | 
			
		||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield)
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
Go-toml provides the following features for using data parsed from TOML documents:
 | 
			
		||||
 | 
			
		||||
* Load TOML documents from files and string data
 | 
			
		||||
* Easily navigate TOML structure using Tree
 | 
			
		||||
* Marshaling and unmarshaling to and from data structures
 | 
			
		||||
* Line & column position data for all parsed elements
 | 
			
		||||
* [Query support similar to JSON-Path](query/)
 | 
			
		||||
* Syntax errors contain line and column numbers
 | 
			
		||||
 | 
			
		||||
## Import
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
import "github.com/pelletier/go-toml"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage example
 | 
			
		||||
 | 
			
		||||
Read a TOML document:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
config, _ := toml.Load(`
 | 
			
		||||
[postgres]
 | 
			
		||||
user = "pelletier"
 | 
			
		||||
password = "mypassword"`)
 | 
			
		||||
// retrieve data directly
 | 
			
		||||
user := config.Get("postgres.user").(string)
 | 
			
		||||
 | 
			
		||||
// or using an intermediate object
 | 
			
		||||
postgresConfig := config.Get("postgres").(*toml.Tree)
 | 
			
		||||
password := postgresConfig.Get("password").(string)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Or use Unmarshal:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
type Postgres struct {
 | 
			
		||||
    User     string
 | 
			
		||||
    Password string
 | 
			
		||||
}
 | 
			
		||||
type Config struct {
 | 
			
		||||
    Postgres Postgres
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
doc := []byte(`
 | 
			
		||||
[Postgres]
 | 
			
		||||
User = "pelletier"
 | 
			
		||||
Password = "mypassword"`)
 | 
			
		||||
 | 
			
		||||
config := Config{}
 | 
			
		||||
toml.Unmarshal(doc, &config)
 | 
			
		||||
fmt.Println("user=", config.Postgres.User)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Or use a query:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
// use a query to gather elements without walking the tree
 | 
			
		||||
q, _ := query.Compile("$..[user,password]")
 | 
			
		||||
results := q.Execute(config)
 | 
			
		||||
for ii, item := range results.Values() {
 | 
			
		||||
    fmt.Printf("Query result %d: %v\n", ii, item)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
The documentation and additional examples are available at
 | 
			
		||||
[godoc.org](http://godoc.org/github.com/pelletier/go-toml).
 | 
			
		||||
 | 
			
		||||
## Tools
 | 
			
		||||
 | 
			
		||||
Go-toml provides two handy command line tools:
 | 
			
		||||
 | 
			
		||||
* `tomll`: Reads TOML files and lints them.
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    go install github.com/pelletier/go-toml/cmd/tomll
 | 
			
		||||
    tomll --help
 | 
			
		||||
    ```
 | 
			
		||||
* `tomljson`: Reads a TOML file and outputs its JSON representation.
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    go install github.com/pelletier/go-toml/cmd/tomljson
 | 
			
		||||
    tomljson --help
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
 * `jsontoml`: Reads a JSON file and outputs a TOML representation.
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    go install github.com/pelletier/go-toml/cmd/jsontoml
 | 
			
		||||
    jsontoml --help
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
### Docker image
 | 
			
		||||
 | 
			
		||||
Those tools are also availble as a Docker image from
 | 
			
		||||
[dockerhub](https://hub.docker.com/r/pelletier/go-toml). For example, to
 | 
			
		||||
use `tomljson`:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
docker run -v $PWD:/workdir pelletier/go-toml tomljson /workdir/example.toml
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Only master (`latest`) and tagged versions are published to dockerhub. You
 | 
			
		||||
can build your own image as usual:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
docker build -t go-toml .
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Contribute
 | 
			
		||||
 | 
			
		||||
Feel free to report bugs and patches using GitHub's pull requests system on
 | 
			
		||||
[pelletier/go-toml](https://github.com/pelletier/go-toml). Any feedback would be
 | 
			
		||||
much appreciated!
 | 
			
		||||
 | 
			
		||||
### Run tests
 | 
			
		||||
 | 
			
		||||
`go test ./...`
 | 
			
		||||
 | 
			
		||||
### Fuzzing
 | 
			
		||||
 | 
			
		||||
The script `./fuzz.sh` is available to
 | 
			
		||||
run [go-fuzz](https://github.com/dvyukov/go-fuzz) on go-toml.
 | 
			
		||||
 | 
			
		||||
## Versioning
 | 
			
		||||
 | 
			
		||||
Go-toml follows [Semantic Versioning](http://semver.org/). The supported version
 | 
			
		||||
of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of
 | 
			
		||||
this document. The last two major versions of Go are supported
 | 
			
		||||
(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)).
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
The MIT License (MIT). Read [LICENSE](LICENSE).
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/pelletier/go-toml/doc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/pelletier/go-toml/doc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
// Package toml is a TOML parser and manipulation library.
 | 
			
		||||
//
 | 
			
		||||
// This version supports the specification as described in
 | 
			
		||||
// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
 | 
			
		||||
//
 | 
			
		||||
// Marshaling
 | 
			
		||||
//
 | 
			
		||||
// Go-toml can marshal and unmarshal TOML documents from and to data
 | 
			
		||||
// structures.
 | 
			
		||||
//
 | 
			
		||||
// TOML document as a tree
 | 
			
		||||
//
 | 
			
		||||
// Go-toml can operate on a TOML document as a tree. Use one of the Load*
 | 
			
		||||
// functions to parse TOML data and obtain a Tree instance, then one of its
 | 
			
		||||
// methods to manipulate the tree.
 | 
			
		||||
//
 | 
			
		||||
// JSONPath-like queries
 | 
			
		||||
//
 | 
			
		||||
// The package github.com/pelletier/go-toml/query implements a system
 | 
			
		||||
// similar to JSONPath to quickly retrieve elements of a TOML document using a
 | 
			
		||||
// single expression. See the package documentation for more information.
 | 
			
		||||
//
 | 
			
		||||
package toml
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/pelletier/go-toml/fuzz.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/pelletier/go-toml/fuzz.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
// +build gofuzz
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
func Fuzz(data []byte) int {
 | 
			
		||||
	tree, err := LoadBytes(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if tree != nil {
 | 
			
		||||
			panic("tree must be nil if there is an error")
 | 
			
		||||
		}
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str, err := tree.ToTomlString()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if str != "" {
 | 
			
		||||
			panic(`str must be "" if there is an error`)
 | 
			
		||||
		}
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tree, err = Load(str)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if tree != nil {
 | 
			
		||||
			panic("tree must be nil if there is an error")
 | 
			
		||||
		}
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/pelletier/go-toml/go.mod
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/pelletier/go-toml/go.mod
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
module github.com/pelletier/go-toml
 | 
			
		||||
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require github.com/davecgh/go-spew v1.1.1
 | 
			
		||||
							
								
								
									
										112
									
								
								vendor/github.com/pelletier/go-toml/keysparsing.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/pelletier/go-toml/keysparsing.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
// Parsing keys handling both bare and quoted keys.
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert the bare key group string to an array.
 | 
			
		||||
// The input supports double quotation and single quotation,
 | 
			
		||||
// but escape sequences are not supported. Lexers must unescape them beforehand.
 | 
			
		||||
func parseKey(key string) ([]string, error) {
 | 
			
		||||
	runes := []rune(key)
 | 
			
		||||
	var groups []string
 | 
			
		||||
 | 
			
		||||
	if len(key) == 0 {
 | 
			
		||||
		return nil, errors.New("empty key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	idx := 0
 | 
			
		||||
	for idx < len(runes) {
 | 
			
		||||
		for ; idx < len(runes) && isSpace(runes[idx]); idx++ {
 | 
			
		||||
			// skip leading whitespace
 | 
			
		||||
		}
 | 
			
		||||
		if idx >= len(runes) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		r := runes[idx]
 | 
			
		||||
		if isValidBareChar(r) {
 | 
			
		||||
			// parse bare key
 | 
			
		||||
			startIdx := idx
 | 
			
		||||
			endIdx := -1
 | 
			
		||||
			idx++
 | 
			
		||||
			for idx < len(runes) {
 | 
			
		||||
				r = runes[idx]
 | 
			
		||||
				if isValidBareChar(r) {
 | 
			
		||||
					idx++
 | 
			
		||||
				} else if r == '.' {
 | 
			
		||||
					endIdx = idx
 | 
			
		||||
					break
 | 
			
		||||
				} else if isSpace(r) {
 | 
			
		||||
					endIdx = idx
 | 
			
		||||
					for ; idx < len(runes) && isSpace(runes[idx]); idx++ {
 | 
			
		||||
						// skip trailing whitespace
 | 
			
		||||
					}
 | 
			
		||||
					if idx < len(runes) && runes[idx] != '.' {
 | 
			
		||||
						return nil, fmt.Errorf("invalid key character after whitespace: %c", runes[idx])
 | 
			
		||||
					}
 | 
			
		||||
					break
 | 
			
		||||
				} else {
 | 
			
		||||
					return nil, fmt.Errorf("invalid bare key character: %c", r)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if endIdx == -1 {
 | 
			
		||||
				endIdx = idx
 | 
			
		||||
			}
 | 
			
		||||
			groups = append(groups, string(runes[startIdx:endIdx]))
 | 
			
		||||
		} else if r == '\'' {
 | 
			
		||||
			// parse single quoted key
 | 
			
		||||
			idx++
 | 
			
		||||
			startIdx := idx
 | 
			
		||||
			for {
 | 
			
		||||
				if idx >= len(runes) {
 | 
			
		||||
					return nil, fmt.Errorf("unclosed single-quoted key")
 | 
			
		||||
				}
 | 
			
		||||
				r = runes[idx]
 | 
			
		||||
				if r == '\'' {
 | 
			
		||||
					groups = append(groups, string(runes[startIdx:idx]))
 | 
			
		||||
					idx++
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				idx++
 | 
			
		||||
			}
 | 
			
		||||
		} else if r == '"' {
 | 
			
		||||
			// parse double quoted key
 | 
			
		||||
			idx++
 | 
			
		||||
			startIdx := idx
 | 
			
		||||
			for {
 | 
			
		||||
				if idx >= len(runes) {
 | 
			
		||||
					return nil, fmt.Errorf("unclosed double-quoted key")
 | 
			
		||||
				}
 | 
			
		||||
				r = runes[idx]
 | 
			
		||||
				if r == '"' {
 | 
			
		||||
					groups = append(groups, string(runes[startIdx:idx]))
 | 
			
		||||
					idx++
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
				idx++
 | 
			
		||||
			}
 | 
			
		||||
		} else if r == '.' {
 | 
			
		||||
			idx++
 | 
			
		||||
			if idx >= len(runes) {
 | 
			
		||||
				return nil, fmt.Errorf("unexpected end of key")
 | 
			
		||||
			}
 | 
			
		||||
			r = runes[idx]
 | 
			
		||||
			if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' {
 | 
			
		||||
				return nil, fmt.Errorf("expecting key part after dot")
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, fmt.Errorf("invalid key character: %c", r)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(groups) == 0 {
 | 
			
		||||
		return nil, fmt.Errorf("empty key")
 | 
			
		||||
	}
 | 
			
		||||
	return groups, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidBareChar(r rune) bool {
 | 
			
		||||
	return isAlphanumeric(r) || r == '-' || isDigit(r)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										807
									
								
								vendor/github.com/pelletier/go-toml/lexer.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										807
									
								
								vendor/github.com/pelletier/go-toml/lexer.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,807 @@
 | 
			
		|||
// TOML lexer.
 | 
			
		||||
//
 | 
			
		||||
// Written using the principles developed by Rob Pike in
 | 
			
		||||
// http://www.youtube.com/watch?v=HxaD_trXwRE
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var dateRegexp *regexp.Regexp
 | 
			
		||||
 | 
			
		||||
// Define state functions
 | 
			
		||||
type tomlLexStateFn func() tomlLexStateFn
 | 
			
		||||
 | 
			
		||||
// Define lexer
 | 
			
		||||
type tomlLexer struct {
 | 
			
		||||
	inputIdx          int
 | 
			
		||||
	input             []rune // Textual source
 | 
			
		||||
	currentTokenStart int
 | 
			
		||||
	currentTokenStop  int
 | 
			
		||||
	tokens            []token
 | 
			
		||||
	brackets          []rune
 | 
			
		||||
	line              int
 | 
			
		||||
	col               int
 | 
			
		||||
	endbufferLine     int
 | 
			
		||||
	endbufferCol      int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Basic read operations on input
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) read() rune {
 | 
			
		||||
	r := l.peek()
 | 
			
		||||
	if r == '\n' {
 | 
			
		||||
		l.endbufferLine++
 | 
			
		||||
		l.endbufferCol = 1
 | 
			
		||||
	} else {
 | 
			
		||||
		l.endbufferCol++
 | 
			
		||||
	}
 | 
			
		||||
	l.inputIdx++
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) next() rune {
 | 
			
		||||
	r := l.read()
 | 
			
		||||
 | 
			
		||||
	if r != eof {
 | 
			
		||||
		l.currentTokenStop++
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) ignore() {
 | 
			
		||||
	l.currentTokenStart = l.currentTokenStop
 | 
			
		||||
	l.line = l.endbufferLine
 | 
			
		||||
	l.col = l.endbufferCol
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) skip() {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.ignore()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) fastForward(n int) {
 | 
			
		||||
	for i := 0; i < n; i++ {
 | 
			
		||||
		l.next()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) emitWithValue(t tokenType, value string) {
 | 
			
		||||
	l.tokens = append(l.tokens, token{
 | 
			
		||||
		Position: Position{l.line, l.col},
 | 
			
		||||
		typ:      t,
 | 
			
		||||
		val:      value,
 | 
			
		||||
	})
 | 
			
		||||
	l.ignore()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) emit(t tokenType) {
 | 
			
		||||
	l.emitWithValue(t, string(l.input[l.currentTokenStart:l.currentTokenStop]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) peek() rune {
 | 
			
		||||
	if l.inputIdx >= len(l.input) {
 | 
			
		||||
		return eof
 | 
			
		||||
	}
 | 
			
		||||
	return l.input[l.inputIdx]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) peekString(size int) string {
 | 
			
		||||
	maxIdx := len(l.input)
 | 
			
		||||
	upperIdx := l.inputIdx + size // FIXME: potential overflow
 | 
			
		||||
	if upperIdx > maxIdx {
 | 
			
		||||
		upperIdx = maxIdx
 | 
			
		||||
	}
 | 
			
		||||
	return string(l.input[l.inputIdx:upperIdx])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) follow(next string) bool {
 | 
			
		||||
	return next == l.peekString(len(next))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error management
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) errorf(format string, args ...interface{}) tomlLexStateFn {
 | 
			
		||||
	l.tokens = append(l.tokens, token{
 | 
			
		||||
		Position: Position{l.line, l.col},
 | 
			
		||||
		typ:      tokenError,
 | 
			
		||||
		val:      fmt.Sprintf(format, args...),
 | 
			
		||||
	})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// State functions
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexVoid() tomlLexStateFn {
 | 
			
		||||
	for {
 | 
			
		||||
		next := l.peek()
 | 
			
		||||
		switch next {
 | 
			
		||||
		case '}': // after '{'
 | 
			
		||||
			return l.lexRightCurlyBrace
 | 
			
		||||
		case '[':
 | 
			
		||||
			return l.lexTableKey
 | 
			
		||||
		case '#':
 | 
			
		||||
			return l.lexComment(l.lexVoid)
 | 
			
		||||
		case '=':
 | 
			
		||||
			return l.lexEqual
 | 
			
		||||
		case '\r':
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case '\n':
 | 
			
		||||
			l.skip()
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if isSpace(next) {
 | 
			
		||||
			l.skip()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if isKeyStartChar(next) {
 | 
			
		||||
			return l.lexKey
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if next == eof {
 | 
			
		||||
			l.next()
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l.emit(tokenEOF)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexRvalue() tomlLexStateFn {
 | 
			
		||||
	for {
 | 
			
		||||
		next := l.peek()
 | 
			
		||||
		switch next {
 | 
			
		||||
		case '.':
 | 
			
		||||
			return l.errorf("cannot start float with a dot")
 | 
			
		||||
		case '=':
 | 
			
		||||
			return l.lexEqual
 | 
			
		||||
		case '[':
 | 
			
		||||
			return l.lexLeftBracket
 | 
			
		||||
		case ']':
 | 
			
		||||
			return l.lexRightBracket
 | 
			
		||||
		case '{':
 | 
			
		||||
			return l.lexLeftCurlyBrace
 | 
			
		||||
		case '}':
 | 
			
		||||
			return l.lexRightCurlyBrace
 | 
			
		||||
		case '#':
 | 
			
		||||
			return l.lexComment(l.lexRvalue)
 | 
			
		||||
		case '"':
 | 
			
		||||
			return l.lexString
 | 
			
		||||
		case '\'':
 | 
			
		||||
			return l.lexLiteralString
 | 
			
		||||
		case ',':
 | 
			
		||||
			return l.lexComma
 | 
			
		||||
		case '\r':
 | 
			
		||||
			fallthrough
 | 
			
		||||
		case '\n':
 | 
			
		||||
			l.skip()
 | 
			
		||||
			if len(l.brackets) > 0 && l.brackets[len(l.brackets)-1] == '[' {
 | 
			
		||||
				return l.lexRvalue
 | 
			
		||||
			}
 | 
			
		||||
			return l.lexVoid
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if l.follow("true") {
 | 
			
		||||
			return l.lexTrue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if l.follow("false") {
 | 
			
		||||
			return l.lexFalse
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if l.follow("inf") {
 | 
			
		||||
			return l.lexInf
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if l.follow("nan") {
 | 
			
		||||
			return l.lexNan
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if isSpace(next) {
 | 
			
		||||
			l.skip()
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if next == eof {
 | 
			
		||||
			l.next()
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		possibleDate := l.peekString(35)
 | 
			
		||||
		dateSubmatches := dateRegexp.FindStringSubmatch(possibleDate)
 | 
			
		||||
		if dateSubmatches != nil && dateSubmatches[0] != "" {
 | 
			
		||||
			l.fastForward(len(dateSubmatches[0]))
 | 
			
		||||
			if dateSubmatches[2] == "" { // no timezone information => local date
 | 
			
		||||
				return l.lexLocalDate
 | 
			
		||||
			}
 | 
			
		||||
			return l.lexDate
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if next == '+' || next == '-' || isDigit(next) {
 | 
			
		||||
			return l.lexNumber
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return l.errorf("no value can start with %c", next)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l.emit(tokenEOF)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.emit(tokenLeftCurlyBrace)
 | 
			
		||||
	l.brackets = append(l.brackets, '{')
 | 
			
		||||
	return l.lexVoid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.emit(tokenRightCurlyBrace)
 | 
			
		||||
	if len(l.brackets) == 0 || l.brackets[len(l.brackets)-1] != '{' {
 | 
			
		||||
		return l.errorf("cannot have '}' here")
 | 
			
		||||
	}
 | 
			
		||||
	l.brackets = l.brackets[:len(l.brackets)-1]
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexDate() tomlLexStateFn {
 | 
			
		||||
	l.emit(tokenDate)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexLocalDate() tomlLexStateFn {
 | 
			
		||||
	l.emit(tokenLocalDate)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexTrue() tomlLexStateFn {
 | 
			
		||||
	l.fastForward(4)
 | 
			
		||||
	l.emit(tokenTrue)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexFalse() tomlLexStateFn {
 | 
			
		||||
	l.fastForward(5)
 | 
			
		||||
	l.emit(tokenFalse)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexInf() tomlLexStateFn {
 | 
			
		||||
	l.fastForward(3)
 | 
			
		||||
	l.emit(tokenInf)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexNan() tomlLexStateFn {
 | 
			
		||||
	l.fastForward(3)
 | 
			
		||||
	l.emit(tokenNan)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexEqual() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.emit(tokenEqual)
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexComma() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.emit(tokenComma)
 | 
			
		||||
	if len(l.brackets) > 0 && l.brackets[len(l.brackets)-1] == '{' {
 | 
			
		||||
		return l.lexVoid
 | 
			
		||||
	}
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse the key and emits its value without escape sequences.
 | 
			
		||||
// bare keys, basic string keys and literal string keys are supported.
 | 
			
		||||
func (l *tomlLexer) lexKey() tomlLexStateFn {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
 | 
			
		||||
	for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() {
 | 
			
		||||
		if r == '"' {
 | 
			
		||||
			l.next()
 | 
			
		||||
			str, err := l.lexStringAsString(`"`, false, true)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return l.errorf(err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			sb.WriteString("\"")
 | 
			
		||||
			sb.WriteString(str)
 | 
			
		||||
			sb.WriteString("\"")
 | 
			
		||||
			l.next()
 | 
			
		||||
			continue
 | 
			
		||||
		} else if r == '\'' {
 | 
			
		||||
			l.next()
 | 
			
		||||
			str, err := l.lexLiteralStringAsString(`'`, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return l.errorf(err.Error())
 | 
			
		||||
			}
 | 
			
		||||
			sb.WriteString("'")
 | 
			
		||||
			sb.WriteString(str)
 | 
			
		||||
			sb.WriteString("'")
 | 
			
		||||
			l.next()
 | 
			
		||||
			continue
 | 
			
		||||
		} else if r == '\n' {
 | 
			
		||||
			return l.errorf("keys cannot contain new lines")
 | 
			
		||||
		} else if isSpace(r) {
 | 
			
		||||
			var str strings.Builder
 | 
			
		||||
			str.WriteString(" ")
 | 
			
		||||
 | 
			
		||||
			// skip trailing whitespace
 | 
			
		||||
			l.next()
 | 
			
		||||
			for r = l.peek(); isSpace(r); r = l.peek() {
 | 
			
		||||
				str.WriteRune(r)
 | 
			
		||||
				l.next()
 | 
			
		||||
			}
 | 
			
		||||
			// break loop if not a dot
 | 
			
		||||
			if r != '.' {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			str.WriteString(".")
 | 
			
		||||
			// skip trailing whitespace after dot
 | 
			
		||||
			l.next()
 | 
			
		||||
			for r = l.peek(); isSpace(r); r = l.peek() {
 | 
			
		||||
				str.WriteRune(r)
 | 
			
		||||
				l.next()
 | 
			
		||||
			}
 | 
			
		||||
			sb.WriteString(str.String())
 | 
			
		||||
			continue
 | 
			
		||||
		} else if r == '.' {
 | 
			
		||||
			// skip
 | 
			
		||||
		} else if !isValidBareChar(r) {
 | 
			
		||||
			return l.errorf("keys cannot contain %c character", r)
 | 
			
		||||
		}
 | 
			
		||||
		sb.WriteRune(r)
 | 
			
		||||
		l.next()
 | 
			
		||||
	}
 | 
			
		||||
	l.emitWithValue(tokenKey, sb.String())
 | 
			
		||||
	return l.lexVoid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexComment(previousState tomlLexStateFn) tomlLexStateFn {
 | 
			
		||||
	return func() tomlLexStateFn {
 | 
			
		||||
		for next := l.peek(); next != '\n' && next != eof; next = l.peek() {
 | 
			
		||||
			if next == '\r' && l.follow("\r\n") {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			l.next()
 | 
			
		||||
		}
 | 
			
		||||
		l.ignore()
 | 
			
		||||
		return previousState
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexLeftBracket() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.emit(tokenLeftBracket)
 | 
			
		||||
	l.brackets = append(l.brackets, '[')
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
 | 
			
		||||
	if discardLeadingNewLine {
 | 
			
		||||
		if l.follow("\r\n") {
 | 
			
		||||
			l.skip()
 | 
			
		||||
			l.skip()
 | 
			
		||||
		} else if l.peek() == '\n' {
 | 
			
		||||
			l.skip()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// find end of string
 | 
			
		||||
	for {
 | 
			
		||||
		if l.follow(terminator) {
 | 
			
		||||
			return sb.String(), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		next := l.peek()
 | 
			
		||||
		if next == eof {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		sb.WriteRune(l.next())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", errors.New("unclosed string")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexLiteralString() tomlLexStateFn {
 | 
			
		||||
	l.skip()
 | 
			
		||||
 | 
			
		||||
	// handle special case for triple-quote
 | 
			
		||||
	terminator := "'"
 | 
			
		||||
	discardLeadingNewLine := false
 | 
			
		||||
	if l.follow("''") {
 | 
			
		||||
		l.skip()
 | 
			
		||||
		l.skip()
 | 
			
		||||
		terminator = "'''"
 | 
			
		||||
		discardLeadingNewLine = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str, err := l.lexLiteralStringAsString(terminator, discardLeadingNewLine)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return l.errorf(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l.emitWithValue(tokenString, str)
 | 
			
		||||
	l.fastForward(len(terminator))
 | 
			
		||||
	l.ignore()
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Lex a string and return the results as a string.
 | 
			
		||||
// Terminator is the substring indicating the end of the token.
 | 
			
		||||
// The resulting string does not include the terminator.
 | 
			
		||||
func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, acceptNewLines bool) (string, error) {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
 | 
			
		||||
	if discardLeadingNewLine {
 | 
			
		||||
		if l.follow("\r\n") {
 | 
			
		||||
			l.skip()
 | 
			
		||||
			l.skip()
 | 
			
		||||
		} else if l.peek() == '\n' {
 | 
			
		||||
			l.skip()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if l.follow(terminator) {
 | 
			
		||||
			return sb.String(), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if l.follow("\\") {
 | 
			
		||||
			l.next()
 | 
			
		||||
			switch l.peek() {
 | 
			
		||||
			case '\r':
 | 
			
		||||
				fallthrough
 | 
			
		||||
			case '\n':
 | 
			
		||||
				fallthrough
 | 
			
		||||
			case '\t':
 | 
			
		||||
				fallthrough
 | 
			
		||||
			case ' ':
 | 
			
		||||
				// skip all whitespace chars following backslash
 | 
			
		||||
				for strings.ContainsRune("\r\n\t ", l.peek()) {
 | 
			
		||||
					l.next()
 | 
			
		||||
				}
 | 
			
		||||
			case '"':
 | 
			
		||||
				sb.WriteString("\"")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case 'n':
 | 
			
		||||
				sb.WriteString("\n")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case 'b':
 | 
			
		||||
				sb.WriteString("\b")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case 'f':
 | 
			
		||||
				sb.WriteString("\f")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case '/':
 | 
			
		||||
				sb.WriteString("/")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case 't':
 | 
			
		||||
				sb.WriteString("\t")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case 'r':
 | 
			
		||||
				sb.WriteString("\r")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case '\\':
 | 
			
		||||
				sb.WriteString("\\")
 | 
			
		||||
				l.next()
 | 
			
		||||
			case 'u':
 | 
			
		||||
				l.next()
 | 
			
		||||
				var code strings.Builder
 | 
			
		||||
				for i := 0; i < 4; i++ {
 | 
			
		||||
					c := l.peek()
 | 
			
		||||
					if !isHexDigit(c) {
 | 
			
		||||
						return "", errors.New("unfinished unicode escape")
 | 
			
		||||
					}
 | 
			
		||||
					l.next()
 | 
			
		||||
					code.WriteRune(c)
 | 
			
		||||
				}
 | 
			
		||||
				intcode, err := strconv.ParseInt(code.String(), 16, 32)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", errors.New("invalid unicode escape: \\u" + code.String())
 | 
			
		||||
				}
 | 
			
		||||
				sb.WriteRune(rune(intcode))
 | 
			
		||||
			case 'U':
 | 
			
		||||
				l.next()
 | 
			
		||||
				var code strings.Builder
 | 
			
		||||
				for i := 0; i < 8; i++ {
 | 
			
		||||
					c := l.peek()
 | 
			
		||||
					if !isHexDigit(c) {
 | 
			
		||||
						return "", errors.New("unfinished unicode escape")
 | 
			
		||||
					}
 | 
			
		||||
					l.next()
 | 
			
		||||
					code.WriteRune(c)
 | 
			
		||||
				}
 | 
			
		||||
				intcode, err := strconv.ParseInt(code.String(), 16, 64)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", errors.New("invalid unicode escape: \\U" + code.String())
 | 
			
		||||
				}
 | 
			
		||||
				sb.WriteRune(rune(intcode))
 | 
			
		||||
			default:
 | 
			
		||||
				return "", errors.New("invalid escape sequence: \\" + string(l.peek()))
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			r := l.peek()
 | 
			
		||||
 | 
			
		||||
			if 0x00 <= r && r <= 0x1F && r != '\t' && !(acceptNewLines && (r == '\n' || r == '\r')) {
 | 
			
		||||
				return "", fmt.Errorf("unescaped control character %U", r)
 | 
			
		||||
			}
 | 
			
		||||
			l.next()
 | 
			
		||||
			sb.WriteRune(r)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if l.peek() == eof {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", errors.New("unclosed string")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexString() tomlLexStateFn {
 | 
			
		||||
	l.skip()
 | 
			
		||||
 | 
			
		||||
	// handle special case for triple-quote
 | 
			
		||||
	terminator := `"`
 | 
			
		||||
	discardLeadingNewLine := false
 | 
			
		||||
	acceptNewLines := false
 | 
			
		||||
	if l.follow(`""`) {
 | 
			
		||||
		l.skip()
 | 
			
		||||
		l.skip()
 | 
			
		||||
		terminator = `"""`
 | 
			
		||||
		discardLeadingNewLine = true
 | 
			
		||||
		acceptNewLines = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str, err := l.lexStringAsString(terminator, discardLeadingNewLine, acceptNewLines)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return l.errorf(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l.emitWithValue(tokenString, str)
 | 
			
		||||
	l.fastForward(len(terminator))
 | 
			
		||||
	l.ignore()
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexTableKey() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
 | 
			
		||||
	if l.peek() == '[' {
 | 
			
		||||
		// token '[[' signifies an array of tables
 | 
			
		||||
		l.next()
 | 
			
		||||
		l.emit(tokenDoubleLeftBracket)
 | 
			
		||||
		return l.lexInsideTableArrayKey
 | 
			
		||||
	}
 | 
			
		||||
	// vanilla table key
 | 
			
		||||
	l.emit(tokenLeftBracket)
 | 
			
		||||
	return l.lexInsideTableKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse the key till "]]", but only bare keys are supported
 | 
			
		||||
func (l *tomlLexer) lexInsideTableArrayKey() tomlLexStateFn {
 | 
			
		||||
	for r := l.peek(); r != eof; r = l.peek() {
 | 
			
		||||
		switch r {
 | 
			
		||||
		case ']':
 | 
			
		||||
			if l.currentTokenStop > l.currentTokenStart {
 | 
			
		||||
				l.emit(tokenKeyGroupArray)
 | 
			
		||||
			}
 | 
			
		||||
			l.next()
 | 
			
		||||
			if l.peek() != ']' {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			l.next()
 | 
			
		||||
			l.emit(tokenDoubleRightBracket)
 | 
			
		||||
			return l.lexVoid
 | 
			
		||||
		case '[':
 | 
			
		||||
			return l.errorf("table array key cannot contain ']'")
 | 
			
		||||
		default:
 | 
			
		||||
			l.next()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return l.errorf("unclosed table array key")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse the key till "]" but only bare keys are supported
 | 
			
		||||
func (l *tomlLexer) lexInsideTableKey() tomlLexStateFn {
 | 
			
		||||
	for r := l.peek(); r != eof; r = l.peek() {
 | 
			
		||||
		switch r {
 | 
			
		||||
		case ']':
 | 
			
		||||
			if l.currentTokenStop > l.currentTokenStart {
 | 
			
		||||
				l.emit(tokenKeyGroup)
 | 
			
		||||
			}
 | 
			
		||||
			l.next()
 | 
			
		||||
			l.emit(tokenRightBracket)
 | 
			
		||||
			return l.lexVoid
 | 
			
		||||
		case '[':
 | 
			
		||||
			return l.errorf("table key cannot contain ']'")
 | 
			
		||||
		default:
 | 
			
		||||
			l.next()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return l.errorf("unclosed table key")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexRightBracket() tomlLexStateFn {
 | 
			
		||||
	l.next()
 | 
			
		||||
	l.emit(tokenRightBracket)
 | 
			
		||||
	if len(l.brackets) == 0 || l.brackets[len(l.brackets)-1] != '[' {
 | 
			
		||||
		return l.errorf("cannot have ']' here")
 | 
			
		||||
	}
 | 
			
		||||
	l.brackets = l.brackets[:len(l.brackets)-1]
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type validRuneFn func(r rune) bool
 | 
			
		||||
 | 
			
		||||
func isValidHexRune(r rune) bool {
 | 
			
		||||
	return r >= 'a' && r <= 'f' ||
 | 
			
		||||
		r >= 'A' && r <= 'F' ||
 | 
			
		||||
		r >= '0' && r <= '9' ||
 | 
			
		||||
		r == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidOctalRune(r rune) bool {
 | 
			
		||||
	return r >= '0' && r <= '7' || r == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidBinaryRune(r rune) bool {
 | 
			
		||||
	return r == '0' || r == '1' || r == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) lexNumber() tomlLexStateFn {
 | 
			
		||||
	r := l.peek()
 | 
			
		||||
 | 
			
		||||
	if r == '0' {
 | 
			
		||||
		follow := l.peekString(2)
 | 
			
		||||
		if len(follow) == 2 {
 | 
			
		||||
			var isValidRune validRuneFn
 | 
			
		||||
			switch follow[1] {
 | 
			
		||||
			case 'x':
 | 
			
		||||
				isValidRune = isValidHexRune
 | 
			
		||||
			case 'o':
 | 
			
		||||
				isValidRune = isValidOctalRune
 | 
			
		||||
			case 'b':
 | 
			
		||||
				isValidRune = isValidBinaryRune
 | 
			
		||||
			default:
 | 
			
		||||
				if follow[1] >= 'a' && follow[1] <= 'z' || follow[1] >= 'A' && follow[1] <= 'Z' {
 | 
			
		||||
					return l.errorf("unknown number base: %s. possible options are x (hex) o (octal) b (binary)", string(follow[1]))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if isValidRune != nil {
 | 
			
		||||
				l.next()
 | 
			
		||||
				l.next()
 | 
			
		||||
				digitSeen := false
 | 
			
		||||
				for {
 | 
			
		||||
					next := l.peek()
 | 
			
		||||
					if !isValidRune(next) {
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					digitSeen = true
 | 
			
		||||
					l.next()
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if !digitSeen {
 | 
			
		||||
					return l.errorf("number needs at least one digit")
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				l.emit(tokenInteger)
 | 
			
		||||
 | 
			
		||||
				return l.lexRvalue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r == '+' || r == '-' {
 | 
			
		||||
		l.next()
 | 
			
		||||
		if l.follow("inf") {
 | 
			
		||||
			return l.lexInf
 | 
			
		||||
		}
 | 
			
		||||
		if l.follow("nan") {
 | 
			
		||||
			return l.lexNan
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pointSeen := false
 | 
			
		||||
	expSeen := false
 | 
			
		||||
	digitSeen := false
 | 
			
		||||
	for {
 | 
			
		||||
		next := l.peek()
 | 
			
		||||
		if next == '.' {
 | 
			
		||||
			if pointSeen {
 | 
			
		||||
				return l.errorf("cannot have two dots in one float")
 | 
			
		||||
			}
 | 
			
		||||
			l.next()
 | 
			
		||||
			if !isDigit(l.peek()) {
 | 
			
		||||
				return l.errorf("float cannot end with a dot")
 | 
			
		||||
			}
 | 
			
		||||
			pointSeen = true
 | 
			
		||||
		} else if next == 'e' || next == 'E' {
 | 
			
		||||
			expSeen = true
 | 
			
		||||
			l.next()
 | 
			
		||||
			r := l.peek()
 | 
			
		||||
			if r == '+' || r == '-' {
 | 
			
		||||
				l.next()
 | 
			
		||||
			}
 | 
			
		||||
		} else if isDigit(next) {
 | 
			
		||||
			digitSeen = true
 | 
			
		||||
			l.next()
 | 
			
		||||
		} else if next == '_' {
 | 
			
		||||
			l.next()
 | 
			
		||||
		} else {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if pointSeen && !digitSeen {
 | 
			
		||||
			return l.errorf("cannot start float with a dot")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !digitSeen {
 | 
			
		||||
		return l.errorf("no digit in that number")
 | 
			
		||||
	}
 | 
			
		||||
	if pointSeen || expSeen {
 | 
			
		||||
		l.emit(tokenFloat)
 | 
			
		||||
	} else {
 | 
			
		||||
		l.emit(tokenInteger)
 | 
			
		||||
	}
 | 
			
		||||
	return l.lexRvalue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *tomlLexer) run() {
 | 
			
		||||
	for state := l.lexVoid; state != nil; {
 | 
			
		||||
		state = state()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// Regexp for all date/time formats supported by TOML.
 | 
			
		||||
	// Group 1: nano precision
 | 
			
		||||
	// Group 2: timezone
 | 
			
		||||
	//
 | 
			
		||||
	// /!\ also matches the empty string
 | 
			
		||||
	//
 | 
			
		||||
	// Example matches:
 | 
			
		||||
	// 1979-05-27T07:32:00Z
 | 
			
		||||
	// 1979-05-27T00:32:00-07:00
 | 
			
		||||
	// 1979-05-27T00:32:00.999999-07:00
 | 
			
		||||
	// 1979-05-27 07:32:00Z
 | 
			
		||||
	// 1979-05-27 00:32:00-07:00
 | 
			
		||||
	// 1979-05-27 00:32:00.999999-07:00
 | 
			
		||||
	// 1979-05-27T07:32:00
 | 
			
		||||
	// 1979-05-27T00:32:00.999999
 | 
			
		||||
	// 1979-05-27 07:32:00
 | 
			
		||||
	// 1979-05-27 00:32:00.999999
 | 
			
		||||
	// 1979-05-27
 | 
			
		||||
	// 07:32:00
 | 
			
		||||
	// 00:32:00.999999
 | 
			
		||||
	dateRegexp = regexp.MustCompile(`^(?:\d{1,4}-\d{2}-\d{2})?(?:[T ]?\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?)?`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Entry point
 | 
			
		||||
func lexToml(inputBytes []byte) []token {
 | 
			
		||||
	runes := bytes.Runes(inputBytes)
 | 
			
		||||
	l := &tomlLexer{
 | 
			
		||||
		input:         runes,
 | 
			
		||||
		tokens:        make([]token, 0, 256),
 | 
			
		||||
		line:          1,
 | 
			
		||||
		col:           1,
 | 
			
		||||
		endbufferLine: 1,
 | 
			
		||||
		endbufferCol:  1,
 | 
			
		||||
	}
 | 
			
		||||
	l.run()
 | 
			
		||||
	return l.tokens
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										281
									
								
								vendor/github.com/pelletier/go-toml/localtime.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								vendor/github.com/pelletier/go-toml/localtime.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,281 @@
 | 
			
		|||
// Implementation of TOML's local date/time.
 | 
			
		||||
// Copied over from https://github.com/googleapis/google-cloud-go/blob/master/civil/civil.go
 | 
			
		||||
// to avoid pulling all the Google dependencies.
 | 
			
		||||
//
 | 
			
		||||
// Copyright 2016 Google LLC
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Package civil implements types for civil time, a time-zone-independent
 | 
			
		||||
// representation of time that follows the rules of the proleptic
 | 
			
		||||
// Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second
 | 
			
		||||
// minutes.
 | 
			
		||||
//
 | 
			
		||||
// Because they lack location information, these types do not represent unique
 | 
			
		||||
// moments or intervals of time. Use time.Time for that purpose.
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A LocalDate represents a date (year, month, day).
 | 
			
		||||
//
 | 
			
		||||
// This type does not include location information, and therefore does not
 | 
			
		||||
// describe a unique 24-hour timespan.
 | 
			
		||||
type LocalDate struct {
 | 
			
		||||
	Year  int        // Year (e.g., 2014).
 | 
			
		||||
	Month time.Month // Month of the year (January = 1, ...).
 | 
			
		||||
	Day   int        // Day of the month, starting at 1.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LocalDateOf returns the LocalDate in which a time occurs in that time's location.
 | 
			
		||||
func LocalDateOf(t time.Time) LocalDate {
 | 
			
		||||
	var d LocalDate
 | 
			
		||||
	d.Year, d.Month, d.Day = t.Date()
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseLocalDate parses a string in RFC3339 full-date format and returns the date value it represents.
 | 
			
		||||
func ParseLocalDate(s string) (LocalDate, error) {
 | 
			
		||||
	t, err := time.Parse("2006-01-02", s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return LocalDate{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return LocalDateOf(t), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns the date in RFC3339 full-date format.
 | 
			
		||||
func (d LocalDate) String() string {
 | 
			
		||||
	return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValid reports whether the date is valid.
 | 
			
		||||
func (d LocalDate) IsValid() bool {
 | 
			
		||||
	return LocalDateOf(d.In(time.UTC)) == d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// In returns the time corresponding to time 00:00:00 of the date in the location.
 | 
			
		||||
//
 | 
			
		||||
// In is always consistent with time.LocalDate, even when time.LocalDate returns a time
 | 
			
		||||
// on a different day. For example, if loc is America/Indiana/Vincennes, then both
 | 
			
		||||
//     time.LocalDate(1955, time.May, 1, 0, 0, 0, 0, loc)
 | 
			
		||||
// and
 | 
			
		||||
//     civil.LocalDate{Year: 1955, Month: time.May, Day: 1}.In(loc)
 | 
			
		||||
// return 23:00:00 on April 30, 1955.
 | 
			
		||||
//
 | 
			
		||||
// In panics if loc is nil.
 | 
			
		||||
func (d LocalDate) In(loc *time.Location) time.Time {
 | 
			
		||||
	return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddDays returns the date that is n days in the future.
 | 
			
		||||
// n can also be negative to go into the past.
 | 
			
		||||
func (d LocalDate) AddDays(n int) LocalDate {
 | 
			
		||||
	return LocalDateOf(d.In(time.UTC).AddDate(0, 0, n))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DaysSince returns the signed number of days between the date and s, not including the end day.
 | 
			
		||||
// This is the inverse operation to AddDays.
 | 
			
		||||
func (d LocalDate) DaysSince(s LocalDate) (days int) {
 | 
			
		||||
	// We convert to Unix time so we do not have to worry about leap seconds:
 | 
			
		||||
	// Unix time increases by exactly 86400 seconds per day.
 | 
			
		||||
	deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix()
 | 
			
		||||
	return int(deltaUnix / 86400)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before reports whether d1 occurs before d2.
 | 
			
		||||
func (d1 LocalDate) Before(d2 LocalDate) bool {
 | 
			
		||||
	if d1.Year != d2.Year {
 | 
			
		||||
		return d1.Year < d2.Year
 | 
			
		||||
	}
 | 
			
		||||
	if d1.Month != d2.Month {
 | 
			
		||||
		return d1.Month < d2.Month
 | 
			
		||||
	}
 | 
			
		||||
	return d1.Day < d2.Day
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// After reports whether d1 occurs after d2.
 | 
			
		||||
func (d1 LocalDate) After(d2 LocalDate) bool {
 | 
			
		||||
	return d2.Before(d1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalText implements the encoding.TextMarshaler interface.
 | 
			
		||||
// The output is the result of d.String().
 | 
			
		||||
func (d LocalDate) MarshalText() ([]byte, error) {
 | 
			
		||||
	return []byte(d.String()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
 | 
			
		||||
// The date is expected to be a string in a format accepted by ParseLocalDate.
 | 
			
		||||
func (d *LocalDate) UnmarshalText(data []byte) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	*d, err = ParseLocalDate(string(data))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A LocalTime represents a time with nanosecond precision.
 | 
			
		||||
//
 | 
			
		||||
// This type does not include location information, and therefore does not
 | 
			
		||||
// describe a unique moment in time.
 | 
			
		||||
//
 | 
			
		||||
// This type exists to represent the TIME type in storage-based APIs like BigQuery.
 | 
			
		||||
// Most operations on Times are unlikely to be meaningful. Prefer the LocalDateTime type.
 | 
			
		||||
type LocalTime struct {
 | 
			
		||||
	Hour       int // The hour of the day in 24-hour format; range [0-23]
 | 
			
		||||
	Minute     int // The minute of the hour; range [0-59]
 | 
			
		||||
	Second     int // The second of the minute; range [0-59]
 | 
			
		||||
	Nanosecond int // The nanosecond of the second; range [0-999999999]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LocalTimeOf returns the LocalTime representing the time of day in which a time occurs
 | 
			
		||||
// in that time's location. It ignores the date.
 | 
			
		||||
func LocalTimeOf(t time.Time) LocalTime {
 | 
			
		||||
	var tm LocalTime
 | 
			
		||||
	tm.Hour, tm.Minute, tm.Second = t.Clock()
 | 
			
		||||
	tm.Nanosecond = t.Nanosecond()
 | 
			
		||||
	return tm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseLocalTime parses a string and returns the time value it represents.
 | 
			
		||||
// ParseLocalTime accepts an extended form of the RFC3339 partial-time format. After
 | 
			
		||||
// the HH:MM:SS part of the string, an optional fractional part may appear,
 | 
			
		||||
// consisting of a decimal point followed by one to nine decimal digits.
 | 
			
		||||
// (RFC3339 admits only one digit after the decimal point).
 | 
			
		||||
func ParseLocalTime(s string) (LocalTime, error) {
 | 
			
		||||
	t, err := time.Parse("15:04:05.999999999", s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return LocalTime{}, err
 | 
			
		||||
	}
 | 
			
		||||
	return LocalTimeOf(t), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns the date in the format described in ParseLocalTime. If Nanoseconds
 | 
			
		||||
// is zero, no fractional part will be generated. Otherwise, the result will
 | 
			
		||||
// end with a fractional part consisting of a decimal point and nine digits.
 | 
			
		||||
func (t LocalTime) String() string {
 | 
			
		||||
	s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
 | 
			
		||||
	if t.Nanosecond == 0 {
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
	return s + fmt.Sprintf(".%09d", t.Nanosecond)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValid reports whether the time is valid.
 | 
			
		||||
func (t LocalTime) IsValid() bool {
 | 
			
		||||
	// Construct a non-zero time.
 | 
			
		||||
	tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC)
 | 
			
		||||
	return LocalTimeOf(tm) == t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalText implements the encoding.TextMarshaler interface.
 | 
			
		||||
// The output is the result of t.String().
 | 
			
		||||
func (t LocalTime) MarshalText() ([]byte, error) {
 | 
			
		||||
	return []byte(t.String()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
 | 
			
		||||
// The time is expected to be a string in a format accepted by ParseLocalTime.
 | 
			
		||||
func (t *LocalTime) UnmarshalText(data []byte) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	*t, err = ParseLocalTime(string(data))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A LocalDateTime represents a date and time.
 | 
			
		||||
//
 | 
			
		||||
// This type does not include location information, and therefore does not
 | 
			
		||||
// describe a unique moment in time.
 | 
			
		||||
type LocalDateTime struct {
 | 
			
		||||
	Date LocalDate
 | 
			
		||||
	Time LocalTime
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Note: We deliberately do not embed LocalDate into LocalDateTime, to avoid promoting AddDays and Sub.
 | 
			
		||||
 | 
			
		||||
// LocalDateTimeOf returns the LocalDateTime in which a time occurs in that time's location.
 | 
			
		||||
func LocalDateTimeOf(t time.Time) LocalDateTime {
 | 
			
		||||
	return LocalDateTime{
 | 
			
		||||
		Date: LocalDateOf(t),
 | 
			
		||||
		Time: LocalTimeOf(t),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseLocalDateTime parses a string and returns the LocalDateTime it represents.
 | 
			
		||||
// ParseLocalDateTime accepts a variant of the RFC3339 date-time format that omits
 | 
			
		||||
// the time offset but includes an optional fractional time, as described in
 | 
			
		||||
// ParseLocalTime. Informally, the accepted format is
 | 
			
		||||
//     YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]
 | 
			
		||||
// where the 'T' may be a lower-case 't'.
 | 
			
		||||
func ParseLocalDateTime(s string) (LocalDateTime, error) {
 | 
			
		||||
	t, err := time.Parse("2006-01-02T15:04:05.999999999", s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t, err = time.Parse("2006-01-02t15:04:05.999999999", s)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return LocalDateTime{}, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return LocalDateTimeOf(t), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String returns the date in the format described in ParseLocalDate.
 | 
			
		||||
func (dt LocalDateTime) String() string {
 | 
			
		||||
	return dt.Date.String() + "T" + dt.Time.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValid reports whether the datetime is valid.
 | 
			
		||||
func (dt LocalDateTime) IsValid() bool {
 | 
			
		||||
	return dt.Date.IsValid() && dt.Time.IsValid()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// In returns the time corresponding to the LocalDateTime in the given location.
 | 
			
		||||
//
 | 
			
		||||
// If the time is missing or ambigous at the location, In returns the same
 | 
			
		||||
// result as time.LocalDate. For example, if loc is America/Indiana/Vincennes, then
 | 
			
		||||
// both
 | 
			
		||||
//     time.LocalDate(1955, time.May, 1, 0, 30, 0, 0, loc)
 | 
			
		||||
// and
 | 
			
		||||
//     civil.LocalDateTime{
 | 
			
		||||
//         civil.LocalDate{Year: 1955, Month: time.May, Day: 1}},
 | 
			
		||||
//         civil.LocalTime{Minute: 30}}.In(loc)
 | 
			
		||||
// return 23:30:00 on April 30, 1955.
 | 
			
		||||
//
 | 
			
		||||
// In panics if loc is nil.
 | 
			
		||||
func (dt LocalDateTime) In(loc *time.Location) time.Time {
 | 
			
		||||
	return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before reports whether dt1 occurs before dt2.
 | 
			
		||||
func (dt1 LocalDateTime) Before(dt2 LocalDateTime) bool {
 | 
			
		||||
	return dt1.In(time.UTC).Before(dt2.In(time.UTC))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// After reports whether dt1 occurs after dt2.
 | 
			
		||||
func (dt1 LocalDateTime) After(dt2 LocalDateTime) bool {
 | 
			
		||||
	return dt2.Before(dt1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalText implements the encoding.TextMarshaler interface.
 | 
			
		||||
// The output is the result of dt.String().
 | 
			
		||||
func (dt LocalDateTime) MarshalText() ([]byte, error) {
 | 
			
		||||
	return []byte(dt.String()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
 | 
			
		||||
// The datetime is expected to be a string in a format accepted by ParseLocalDateTime
 | 
			
		||||
func (dt *LocalDateTime) UnmarshalText(data []byte) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	*dt, err = ParseLocalDateTime(string(data))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1269
									
								
								vendor/github.com/pelletier/go-toml/marshal.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1269
									
								
								vendor/github.com/pelletier/go-toml/marshal.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										493
									
								
								vendor/github.com/pelletier/go-toml/parser.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										493
									
								
								vendor/github.com/pelletier/go-toml/parser.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,493 @@
 | 
			
		|||
// TOML Parser.
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tomlParser struct {
 | 
			
		||||
	flowIdx       int
 | 
			
		||||
	flow          []token
 | 
			
		||||
	tree          *Tree
 | 
			
		||||
	currentTable  []string
 | 
			
		||||
	seenTableKeys []string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tomlParserStateFn func() tomlParserStateFn
 | 
			
		||||
 | 
			
		||||
// Formats and panics an error message based on a token
 | 
			
		||||
func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) {
 | 
			
		||||
	panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) run() {
 | 
			
		||||
	for state := p.parseStart; state != nil; {
 | 
			
		||||
		state = state()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) peek() *token {
 | 
			
		||||
	if p.flowIdx >= len(p.flow) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return &p.flow[p.flowIdx]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) assume(typ tokenType) {
 | 
			
		||||
	tok := p.getToken()
 | 
			
		||||
	if tok == nil {
 | 
			
		||||
		p.raiseError(tok, "was expecting token %s, but token stream is empty", tok)
 | 
			
		||||
	}
 | 
			
		||||
	if tok.typ != typ {
 | 
			
		||||
		p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) getToken() *token {
 | 
			
		||||
	tok := p.peek()
 | 
			
		||||
	if tok == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.flowIdx++
 | 
			
		||||
	return tok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseStart() tomlParserStateFn {
 | 
			
		||||
	tok := p.peek()
 | 
			
		||||
 | 
			
		||||
	// end of stream, parsing is finished
 | 
			
		||||
	if tok == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch tok.typ {
 | 
			
		||||
	case tokenDoubleLeftBracket:
 | 
			
		||||
		return p.parseGroupArray
 | 
			
		||||
	case tokenLeftBracket:
 | 
			
		||||
		return p.parseGroup
 | 
			
		||||
	case tokenKey:
 | 
			
		||||
		return p.parseAssign
 | 
			
		||||
	case tokenEOF:
 | 
			
		||||
		return nil
 | 
			
		||||
	case tokenError:
 | 
			
		||||
		p.raiseError(tok, "parsing error: %s", tok.String())
 | 
			
		||||
	default:
 | 
			
		||||
		p.raiseError(tok, "unexpected token %s", tok.typ)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseGroupArray() tomlParserStateFn {
 | 
			
		||||
	startToken := p.getToken() // discard the [[
 | 
			
		||||
	key := p.getToken()
 | 
			
		||||
	if key.typ != tokenKeyGroupArray {
 | 
			
		||||
		p.raiseError(key, "unexpected token %s, was expecting a table array key", key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get or create table array element at the indicated part in the path
 | 
			
		||||
	keys, err := parseKey(key.val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p.raiseError(key, "invalid table array key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries
 | 
			
		||||
	destTree := p.tree.GetPath(keys)
 | 
			
		||||
	var array []*Tree
 | 
			
		||||
	if destTree == nil {
 | 
			
		||||
		array = make([]*Tree, 0)
 | 
			
		||||
	} else if target, ok := destTree.([]*Tree); ok && target != nil {
 | 
			
		||||
		array = destTree.([]*Tree)
 | 
			
		||||
	} else {
 | 
			
		||||
		p.raiseError(key, "key %s is already assigned and not of type table array", key)
 | 
			
		||||
	}
 | 
			
		||||
	p.currentTable = keys
 | 
			
		||||
 | 
			
		||||
	// add a new tree to the end of the table array
 | 
			
		||||
	newTree := newTree()
 | 
			
		||||
	newTree.position = startToken.Position
 | 
			
		||||
	array = append(array, newTree)
 | 
			
		||||
	p.tree.SetPath(p.currentTable, array)
 | 
			
		||||
 | 
			
		||||
	// remove all keys that were children of this table array
 | 
			
		||||
	prefix := key.val + "."
 | 
			
		||||
	found := false
 | 
			
		||||
	for ii := 0; ii < len(p.seenTableKeys); {
 | 
			
		||||
		tableKey := p.seenTableKeys[ii]
 | 
			
		||||
		if strings.HasPrefix(tableKey, prefix) {
 | 
			
		||||
			p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...)
 | 
			
		||||
		} else {
 | 
			
		||||
			found = (tableKey == key.val)
 | 
			
		||||
			ii++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// keep this key name from use by other kinds of assignments
 | 
			
		||||
	if !found {
 | 
			
		||||
		p.seenTableKeys = append(p.seenTableKeys, key.val)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// move to next parser state
 | 
			
		||||
	p.assume(tokenDoubleRightBracket)
 | 
			
		||||
	return p.parseStart
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseGroup() tomlParserStateFn {
 | 
			
		||||
	startToken := p.getToken() // discard the [
 | 
			
		||||
	key := p.getToken()
 | 
			
		||||
	if key.typ != tokenKeyGroup {
 | 
			
		||||
		p.raiseError(key, "unexpected token %s, was expecting a table key", key)
 | 
			
		||||
	}
 | 
			
		||||
	for _, item := range p.seenTableKeys {
 | 
			
		||||
		if item == key.val {
 | 
			
		||||
			p.raiseError(key, "duplicated tables")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.seenTableKeys = append(p.seenTableKeys, key.val)
 | 
			
		||||
	keys, err := parseKey(key.val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p.raiseError(key, "invalid table array key: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := p.tree.createSubTree(keys, startToken.Position); err != nil {
 | 
			
		||||
		p.raiseError(key, "%s", err)
 | 
			
		||||
	}
 | 
			
		||||
	destTree := p.tree.GetPath(keys)
 | 
			
		||||
	if target, ok := destTree.(*Tree); ok && target != nil && target.inline {
 | 
			
		||||
		p.raiseError(key, "could not re-define exist inline table or its sub-table : %s",
 | 
			
		||||
			strings.Join(keys, "."))
 | 
			
		||||
	}
 | 
			
		||||
	p.assume(tokenRightBracket)
 | 
			
		||||
	p.currentTable = keys
 | 
			
		||||
	return p.parseStart
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseAssign() tomlParserStateFn {
 | 
			
		||||
	key := p.getToken()
 | 
			
		||||
	p.assume(tokenEqual)
 | 
			
		||||
 | 
			
		||||
	parsedKey, err := parseKey(key.val)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p.raiseError(key, "invalid key: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value := p.parseRvalue()
 | 
			
		||||
	var tableKey []string
 | 
			
		||||
	if len(p.currentTable) > 0 {
 | 
			
		||||
		tableKey = p.currentTable
 | 
			
		||||
	} else {
 | 
			
		||||
		tableKey = []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prefixKey := parsedKey[0 : len(parsedKey)-1]
 | 
			
		||||
	tableKey = append(tableKey, prefixKey...)
 | 
			
		||||
 | 
			
		||||
	// find the table to assign, looking out for arrays of tables
 | 
			
		||||
	var targetNode *Tree
 | 
			
		||||
	switch node := p.tree.GetPath(tableKey).(type) {
 | 
			
		||||
	case []*Tree:
 | 
			
		||||
		targetNode = node[len(node)-1]
 | 
			
		||||
	case *Tree:
 | 
			
		||||
		targetNode = node
 | 
			
		||||
	case nil:
 | 
			
		||||
		// create intermediate
 | 
			
		||||
		if err := p.tree.createSubTree(tableKey, key.Position); err != nil {
 | 
			
		||||
			p.raiseError(key, "could not create intermediate group: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		targetNode = p.tree.GetPath(tableKey).(*Tree)
 | 
			
		||||
	default:
 | 
			
		||||
		p.raiseError(key, "Unknown table type for path: %s",
 | 
			
		||||
			strings.Join(tableKey, "."))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if targetNode.inline {
 | 
			
		||||
		p.raiseError(key, "could not add key or sub-table to exist inline table or its sub-table : %s",
 | 
			
		||||
			strings.Join(tableKey, "."))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// assign value to the found table
 | 
			
		||||
	keyVal := parsedKey[len(parsedKey)-1]
 | 
			
		||||
	localKey := []string{keyVal}
 | 
			
		||||
	finalKey := append(tableKey, keyVal)
 | 
			
		||||
	if targetNode.GetPath(localKey) != nil {
 | 
			
		||||
		p.raiseError(key, "The following key was defined twice: %s",
 | 
			
		||||
			strings.Join(finalKey, "."))
 | 
			
		||||
	}
 | 
			
		||||
	var toInsert interface{}
 | 
			
		||||
 | 
			
		||||
	switch value.(type) {
 | 
			
		||||
	case *Tree, []*Tree:
 | 
			
		||||
		toInsert = value
 | 
			
		||||
	default:
 | 
			
		||||
		toInsert = &tomlValue{value: value, position: key.Position}
 | 
			
		||||
	}
 | 
			
		||||
	targetNode.values[keyVal] = toInsert
 | 
			
		||||
	return p.parseStart
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var numberUnderscoreInvalidRegexp *regexp.Regexp
 | 
			
		||||
var hexNumberUnderscoreInvalidRegexp *regexp.Regexp
 | 
			
		||||
 | 
			
		||||
func numberContainsInvalidUnderscore(value string) error {
 | 
			
		||||
	if numberUnderscoreInvalidRegexp.MatchString(value) {
 | 
			
		||||
		return errors.New("invalid use of _ in number")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hexNumberContainsInvalidUnderscore(value string) error {
 | 
			
		||||
	if hexNumberUnderscoreInvalidRegexp.MatchString(value) {
 | 
			
		||||
		return errors.New("invalid use of _ in hex number")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cleanupNumberToken(value string) string {
 | 
			
		||||
	cleanedVal := strings.Replace(value, "_", "", -1)
 | 
			
		||||
	return cleanedVal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseRvalue() interface{} {
 | 
			
		||||
	tok := p.getToken()
 | 
			
		||||
	if tok == nil || tok.typ == tokenEOF {
 | 
			
		||||
		p.raiseError(tok, "expecting a value")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch tok.typ {
 | 
			
		||||
	case tokenString:
 | 
			
		||||
		return tok.val
 | 
			
		||||
	case tokenTrue:
 | 
			
		||||
		return true
 | 
			
		||||
	case tokenFalse:
 | 
			
		||||
		return false
 | 
			
		||||
	case tokenInf:
 | 
			
		||||
		if tok.val[0] == '-' {
 | 
			
		||||
			return math.Inf(-1)
 | 
			
		||||
		}
 | 
			
		||||
		return math.Inf(1)
 | 
			
		||||
	case tokenNan:
 | 
			
		||||
		return math.NaN()
 | 
			
		||||
	case tokenInteger:
 | 
			
		||||
		cleanedVal := cleanupNumberToken(tok.val)
 | 
			
		||||
		var err error
 | 
			
		||||
		var val int64
 | 
			
		||||
		if len(cleanedVal) >= 3 && cleanedVal[0] == '0' {
 | 
			
		||||
			switch cleanedVal[1] {
 | 
			
		||||
			case 'x':
 | 
			
		||||
				err = hexNumberContainsInvalidUnderscore(tok.val)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					p.raiseError(tok, "%s", err)
 | 
			
		||||
				}
 | 
			
		||||
				val, err = strconv.ParseInt(cleanedVal[2:], 16, 64)
 | 
			
		||||
			case 'o':
 | 
			
		||||
				err = numberContainsInvalidUnderscore(tok.val)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					p.raiseError(tok, "%s", err)
 | 
			
		||||
				}
 | 
			
		||||
				val, err = strconv.ParseInt(cleanedVal[2:], 8, 64)
 | 
			
		||||
			case 'b':
 | 
			
		||||
				err = numberContainsInvalidUnderscore(tok.val)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					p.raiseError(tok, "%s", err)
 | 
			
		||||
				}
 | 
			
		||||
				val, err = strconv.ParseInt(cleanedVal[2:], 2, 64)
 | 
			
		||||
			default:
 | 
			
		||||
				panic("invalid base") // the lexer should catch this first
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			err = numberContainsInvalidUnderscore(tok.val)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				p.raiseError(tok, "%s", err)
 | 
			
		||||
			}
 | 
			
		||||
			val, err = strconv.ParseInt(cleanedVal, 10, 64)
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.raiseError(tok, "%s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return val
 | 
			
		||||
	case tokenFloat:
 | 
			
		||||
		err := numberContainsInvalidUnderscore(tok.val)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.raiseError(tok, "%s", err)
 | 
			
		||||
		}
 | 
			
		||||
		cleanedVal := cleanupNumberToken(tok.val)
 | 
			
		||||
		val, err := strconv.ParseFloat(cleanedVal, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.raiseError(tok, "%s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return val
 | 
			
		||||
	case tokenDate:
 | 
			
		||||
		layout := time.RFC3339Nano
 | 
			
		||||
		if !strings.Contains(tok.val, "T") {
 | 
			
		||||
			layout = strings.Replace(layout, "T", " ", 1)
 | 
			
		||||
		}
 | 
			
		||||
		val, err := time.ParseInLocation(layout, tok.val, time.UTC)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.raiseError(tok, "%s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return val
 | 
			
		||||
	case tokenLocalDate:
 | 
			
		||||
		v := strings.Replace(tok.val, " ", "T", -1)
 | 
			
		||||
		isDateTime := false
 | 
			
		||||
		isTime := false
 | 
			
		||||
		for _, c := range v {
 | 
			
		||||
			if c == 'T' || c == 't' {
 | 
			
		||||
				isDateTime = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if c == ':' {
 | 
			
		||||
				isTime = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var val interface{}
 | 
			
		||||
		var err error
 | 
			
		||||
 | 
			
		||||
		if isDateTime {
 | 
			
		||||
			val, err = ParseLocalDateTime(v)
 | 
			
		||||
		} else if isTime {
 | 
			
		||||
			val, err = ParseLocalTime(v)
 | 
			
		||||
		} else {
 | 
			
		||||
			val, err = ParseLocalDate(v)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			p.raiseError(tok, "%s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return val
 | 
			
		||||
	case tokenLeftBracket:
 | 
			
		||||
		return p.parseArray()
 | 
			
		||||
	case tokenLeftCurlyBrace:
 | 
			
		||||
		return p.parseInlineTable()
 | 
			
		||||
	case tokenEqual:
 | 
			
		||||
		p.raiseError(tok, "cannot have multiple equals for the same key")
 | 
			
		||||
	case tokenError:
 | 
			
		||||
		p.raiseError(tok, "%s", tok)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.raiseError(tok, "never reached")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tokenIsComma(t *token) bool {
 | 
			
		||||
	return t != nil && t.typ == tokenComma
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseInlineTable() *Tree {
 | 
			
		||||
	tree := newTree()
 | 
			
		||||
	var previous *token
 | 
			
		||||
Loop:
 | 
			
		||||
	for {
 | 
			
		||||
		follow := p.peek()
 | 
			
		||||
		if follow == nil || follow.typ == tokenEOF {
 | 
			
		||||
			p.raiseError(follow, "unterminated inline table")
 | 
			
		||||
		}
 | 
			
		||||
		switch follow.typ {
 | 
			
		||||
		case tokenRightCurlyBrace:
 | 
			
		||||
			p.getToken()
 | 
			
		||||
			break Loop
 | 
			
		||||
		case tokenKey, tokenInteger, tokenString:
 | 
			
		||||
			if !tokenIsComma(previous) && previous != nil {
 | 
			
		||||
				p.raiseError(follow, "comma expected between fields in inline table")
 | 
			
		||||
			}
 | 
			
		||||
			key := p.getToken()
 | 
			
		||||
			p.assume(tokenEqual)
 | 
			
		||||
 | 
			
		||||
			parsedKey, err := parseKey(key.val)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				p.raiseError(key, "invalid key: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			value := p.parseRvalue()
 | 
			
		||||
			tree.SetPath(parsedKey, value)
 | 
			
		||||
		case tokenComma:
 | 
			
		||||
			if tokenIsComma(previous) {
 | 
			
		||||
				p.raiseError(follow, "need field between two commas in inline table")
 | 
			
		||||
			}
 | 
			
		||||
			p.getToken()
 | 
			
		||||
		default:
 | 
			
		||||
			p.raiseError(follow, "unexpected token type in inline table: %s", follow.String())
 | 
			
		||||
		}
 | 
			
		||||
		previous = follow
 | 
			
		||||
	}
 | 
			
		||||
	if tokenIsComma(previous) {
 | 
			
		||||
		p.raiseError(previous, "trailing comma at the end of inline table")
 | 
			
		||||
	}
 | 
			
		||||
	tree.inline = true
 | 
			
		||||
	return tree
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *tomlParser) parseArray() interface{} {
 | 
			
		||||
	var array []interface{}
 | 
			
		||||
	arrayType := reflect.TypeOf(newTree())
 | 
			
		||||
	for {
 | 
			
		||||
		follow := p.peek()
 | 
			
		||||
		if follow == nil || follow.typ == tokenEOF {
 | 
			
		||||
			p.raiseError(follow, "unterminated array")
 | 
			
		||||
		}
 | 
			
		||||
		if follow.typ == tokenRightBracket {
 | 
			
		||||
			p.getToken()
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		val := p.parseRvalue()
 | 
			
		||||
		if reflect.TypeOf(val) != arrayType {
 | 
			
		||||
			arrayType = nil
 | 
			
		||||
		}
 | 
			
		||||
		array = append(array, val)
 | 
			
		||||
		follow = p.peek()
 | 
			
		||||
		if follow == nil || follow.typ == tokenEOF {
 | 
			
		||||
			p.raiseError(follow, "unterminated array")
 | 
			
		||||
		}
 | 
			
		||||
		if follow.typ != tokenRightBracket && follow.typ != tokenComma {
 | 
			
		||||
			p.raiseError(follow, "missing comma")
 | 
			
		||||
		}
 | 
			
		||||
		if follow.typ == tokenComma {
 | 
			
		||||
			p.getToken()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if the array is a mixed-type array or its length is 0,
 | 
			
		||||
	// don't convert it to a table array
 | 
			
		||||
	if len(array) <= 0 {
 | 
			
		||||
		arrayType = nil
 | 
			
		||||
	}
 | 
			
		||||
	// An array of Trees is actually an array of inline
 | 
			
		||||
	// tables, which is a shorthand for a table array. If the
 | 
			
		||||
	// array was not converted from []interface{} to []*Tree,
 | 
			
		||||
	// the two notations would not be equivalent.
 | 
			
		||||
	if arrayType == reflect.TypeOf(newTree()) {
 | 
			
		||||
		tomlArray := make([]*Tree, len(array))
 | 
			
		||||
		for i, v := range array {
 | 
			
		||||
			tomlArray[i] = v.(*Tree)
 | 
			
		||||
		}
 | 
			
		||||
		return tomlArray
 | 
			
		||||
	}
 | 
			
		||||
	return array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseToml(flow []token) *Tree {
 | 
			
		||||
	result := newTree()
 | 
			
		||||
	result.position = Position{1, 1}
 | 
			
		||||
	parser := &tomlParser{
 | 
			
		||||
		flowIdx:       0,
 | 
			
		||||
		flow:          flow,
 | 
			
		||||
		tree:          result,
 | 
			
		||||
		currentTable:  make([]string, 0),
 | 
			
		||||
		seenTableKeys: make([]string, 0),
 | 
			
		||||
	}
 | 
			
		||||
	parser.run()
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d])|_$|^_`)
 | 
			
		||||
	hexNumberUnderscoreInvalidRegexp = regexp.MustCompile(`(^0x_)|([^\da-f]_|_[^\da-f])|_$|^_`)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								vendor/github.com/pelletier/go-toml/position.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/pelletier/go-toml/position.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
// Position support for go-toml
 | 
			
		||||
 | 
			
		||||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Position of a document element within a TOML document.
 | 
			
		||||
//
 | 
			
		||||
// Line and Col are both 1-indexed positions for the element's line number and
 | 
			
		||||
// column number, respectively.  Values of zero or less will cause Invalid(),
 | 
			
		||||
// to return true.
 | 
			
		||||
type Position struct {
 | 
			
		||||
	Line int // line within the document
 | 
			
		||||
	Col  int // column within the line
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String representation of the position.
 | 
			
		||||
// Displays 1-indexed line and column numbers.
 | 
			
		||||
func (p Position) String() string {
 | 
			
		||||
	return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Invalid returns whether or not the position is valid (i.e. with negative or
 | 
			
		||||
// null values)
 | 
			
		||||
func (p Position) Invalid() bool {
 | 
			
		||||
	return p.Line <= 0 || p.Col <= 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										134
									
								
								vendor/github.com/pelletier/go-toml/token.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/pelletier/go-toml/token.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
package toml
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
 | 
			
		||||
// Define tokens
 | 
			
		||||
type tokenType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	eof = -(iota + 1)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	tokenError tokenType = iota
 | 
			
		||||
	tokenEOF
 | 
			
		||||
	tokenComment
 | 
			
		||||
	tokenKey
 | 
			
		||||
	tokenString
 | 
			
		||||
	tokenInteger
 | 
			
		||||
	tokenTrue
 | 
			
		||||
	tokenFalse
 | 
			
		||||
	tokenFloat
 | 
			
		||||
	tokenInf
 | 
			
		||||
	tokenNan
 | 
			
		||||
	tokenEqual
 | 
			
		||||
	tokenLeftBracket
 | 
			
		||||
	tokenRightBracket
 | 
			
		||||
	tokenLeftCurlyBrace
 | 
			
		||||
	tokenRightCurlyBrace
 | 
			
		||||
	tokenLeftParen
 | 
			
		||||
	tokenRightParen
 | 
			
		||||
	tokenDoubleLeftBracket
 | 
			
		||||
	tokenDoubleRightBracket
 | 
			
		||||
	tokenDate
 | 
			
		||||
	tokenLocalDate
 | 
			
		||||
	tokenKeyGroup
 | 
			
		||||
	tokenKeyGroupArray
 | 
			
		||||
	tokenComma
 | 
			
		||||
	tokenColon
 | 
			
		||||
	tokenDollar
 | 
			
		||||
	tokenStar
 | 
			
		||||
	tokenQuestion
 | 
			
		||||
	tokenDot
 | 
			
		||||
	tokenDotDot
 | 
			
		||||
	tokenEOL
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var tokenTypeNames = []string{
 | 
			
		||||
	"Error",
 | 
			
		||||
	"EOF",
 | 
			
		||||
	"Comment",
 | 
			
		||||
	"Key",
 | 
			
		||||
	"String",
 | 
			
		||||
	"Integer",
 | 
			
		||||
	"True",
 | 
			
		||||
	"False",
 | 
			
		||||
	"Float",
 | 
			
		||||
	"Inf",
 | 
			
		||||
	"NaN",
 | 
			
		||||
	"=",
 | 
			
		||||
	"[",
 | 
			
		||||
	"]",
 | 
			
		||||
	"{",
 | 
			
		||||
	"}",
 | 
			
		||||
	"(",
 | 
			
		||||
	")",
 | 
			
		||||
	"]]",
 | 
			
		||||
	"[[",
 | 
			
		||||
	"LocalDate",
 | 
			
		||||
	"LocalDate",
 | 
			
		||||
	"KeyGroup",
 | 
			
		||||
	"KeyGroupArray",
 | 
			
		||||
	",",
 | 
			
		||||
	":",
 | 
			
		||||
	"$",
 | 
			
		||||
	"*",
 | 
			
		||||
	"?",
 | 
			
		||||
	".",
 | 
			
		||||
	"..",
 | 
			
		||||
	"EOL",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type token struct {
 | 
			
		||||
	Position
 | 
			
		||||
	typ tokenType
 | 
			
		||||
	val string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (tt tokenType) String() string {
 | 
			
		||||
	idx := int(tt)
 | 
			
		||||
	if idx < len(tokenTypeNames) {
 | 
			
		||||
		return tokenTypeNames[idx]
 | 
			
		||||
	}
 | 
			
		||||
	return "Unknown"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t token) String() string {
 | 
			
		||||
	switch t.typ {
 | 
			
		||||
	case tokenEOF:
 | 
			
		||||
		return "EOF"
 | 
			
		||||
	case tokenError:
 | 
			
		||||
		return t.val
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%q", t.val)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isSpace(r rune) bool {
 | 
			
		||||
	return r == ' ' || r == '\t'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isAlphanumeric(r rune) bool {
 | 
			
		||||
	return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isKeyChar(r rune) bool {
 | 
			
		||||
	// Keys start with the first character that isn't whitespace or [ and end
 | 
			
		||||
	// with the last non-whitespace character before the equals sign. Keys
 | 
			
		||||
	// cannot contain a # character."
 | 
			
		||||
	return !(r == '\r' || r == '\n' || r == eof || r == '=')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isKeyStartChar(r rune) bool {
 | 
			
		||||
	return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '[')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isDigit(r rune) bool {
 | 
			
		||||
	return '0' <= r && r <= '9'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isHexDigit(r rune) bool {
 | 
			
		||||
	return isDigit(r) ||
 | 
			
		||||
		(r >= 'a' && r <= 'f') ||
 | 
			
		||||
		(r >= 'A' && r <= 'F')
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										529
									
								
								vendor/github.com/pelletier/go-toml/toml.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										529
									
								
								vendor/github.com/pelletier/go-toml/toml.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,529 @@
 | 
			
		|||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tomlValue struct {
 | 
			
		||||
	value     interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
 | 
			
		||||
	comment   string
 | 
			
		||||
	commented bool
 | 
			
		||||
	multiline bool
 | 
			
		||||
	position  Position
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tree is the result of the parsing of a TOML file.
 | 
			
		||||
type Tree struct {
 | 
			
		||||
	values    map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
 | 
			
		||||
	comment   string
 | 
			
		||||
	commented bool
 | 
			
		||||
	inline    bool
 | 
			
		||||
	position  Position
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTree() *Tree {
 | 
			
		||||
	return newTreeWithPosition(Position{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTreeWithPosition(pos Position) *Tree {
 | 
			
		||||
	return &Tree{
 | 
			
		||||
		values:   make(map[string]interface{}),
 | 
			
		||||
		position: pos,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TreeFromMap initializes a new Tree object using the given map.
 | 
			
		||||
func TreeFromMap(m map[string]interface{}) (*Tree, error) {
 | 
			
		||||
	result, err := toTree(m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return result.(*Tree), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Position returns the position of the tree.
 | 
			
		||||
func (t *Tree) Position() Position {
 | 
			
		||||
	return t.position
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Has returns a boolean indicating if the given key exists.
 | 
			
		||||
func (t *Tree) Has(key string) bool {
 | 
			
		||||
	if key == "" {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return t.HasPath(strings.Split(key, "."))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasPath returns true if the given path of keys exists, false otherwise.
 | 
			
		||||
func (t *Tree) HasPath(keys []string) bool {
 | 
			
		||||
	return t.GetPath(keys) != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Keys returns the keys of the toplevel tree (does not recurse).
 | 
			
		||||
func (t *Tree) Keys() []string {
 | 
			
		||||
	keys := make([]string, len(t.values))
 | 
			
		||||
	i := 0
 | 
			
		||||
	for k := range t.values {
 | 
			
		||||
		keys[i] = k
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
	return keys
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the value at key in the Tree.
 | 
			
		||||
// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
 | 
			
		||||
// If you need to retrieve non-bare keys, use GetPath.
 | 
			
		||||
// Returns nil if the path does not exist in the tree.
 | 
			
		||||
// If keys is of length zero, the current tree is returned.
 | 
			
		||||
func (t *Tree) Get(key string) interface{} {
 | 
			
		||||
	if key == "" {
 | 
			
		||||
		return t
 | 
			
		||||
	}
 | 
			
		||||
	return t.GetPath(strings.Split(key, "."))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPath returns the element in the tree indicated by 'keys'.
 | 
			
		||||
// If keys is of length zero, the current tree is returned.
 | 
			
		||||
func (t *Tree) GetPath(keys []string) interface{} {
 | 
			
		||||
	if len(keys) == 0 {
 | 
			
		||||
		return t
 | 
			
		||||
	}
 | 
			
		||||
	subtree := t
 | 
			
		||||
	for _, intermediateKey := range keys[:len(keys)-1] {
 | 
			
		||||
		value, exists := subtree.values[intermediateKey]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		switch node := value.(type) {
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			subtree = node
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			// go to most recent element
 | 
			
		||||
			if len(node) == 0 {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			subtree = node[len(node)-1]
 | 
			
		||||
		default:
 | 
			
		||||
			return nil // cannot navigate through other node types
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// branch based on final node type
 | 
			
		||||
	switch node := subtree.values[keys[len(keys)-1]].(type) {
 | 
			
		||||
	case *tomlValue:
 | 
			
		||||
		return node.value
 | 
			
		||||
	default:
 | 
			
		||||
		return node
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetArray returns the value at key in the Tree.
 | 
			
		||||
// It returns []string, []int64, etc type if key has homogeneous lists
 | 
			
		||||
// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
 | 
			
		||||
// Returns nil if the path does not exist in the tree.
 | 
			
		||||
// If keys is of length zero, the current tree is returned.
 | 
			
		||||
func (t *Tree) GetArray(key string) interface{} {
 | 
			
		||||
	if key == "" {
 | 
			
		||||
		return t
 | 
			
		||||
	}
 | 
			
		||||
	return t.GetArrayPath(strings.Split(key, "."))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetArrayPath returns the element in the tree indicated by 'keys'.
 | 
			
		||||
// If keys is of length zero, the current tree is returned.
 | 
			
		||||
func (t *Tree) GetArrayPath(keys []string) interface{} {
 | 
			
		||||
	if len(keys) == 0 {
 | 
			
		||||
		return t
 | 
			
		||||
	}
 | 
			
		||||
	subtree := t
 | 
			
		||||
	for _, intermediateKey := range keys[:len(keys)-1] {
 | 
			
		||||
		value, exists := subtree.values[intermediateKey]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		switch node := value.(type) {
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			subtree = node
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			// go to most recent element
 | 
			
		||||
			if len(node) == 0 {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			subtree = node[len(node)-1]
 | 
			
		||||
		default:
 | 
			
		||||
			return nil // cannot navigate through other node types
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// branch based on final node type
 | 
			
		||||
	switch node := subtree.values[keys[len(keys)-1]].(type) {
 | 
			
		||||
	case *tomlValue:
 | 
			
		||||
		switch n := node.value.(type) {
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			return getArray(n)
 | 
			
		||||
		default:
 | 
			
		||||
			return node.value
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return node
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// if homogeneous array, then return slice type object over []interface{}
 | 
			
		||||
func getArray(n []interface{}) interface{} {
 | 
			
		||||
	var s []string
 | 
			
		||||
	var i64 []int64
 | 
			
		||||
	var f64 []float64
 | 
			
		||||
	var bl []bool
 | 
			
		||||
	for _, value := range n {
 | 
			
		||||
		switch v := value.(type) {
 | 
			
		||||
		case string:
 | 
			
		||||
			s = append(s, v)
 | 
			
		||||
		case int64:
 | 
			
		||||
			i64 = append(i64, v)
 | 
			
		||||
		case float64:
 | 
			
		||||
			f64 = append(f64, v)
 | 
			
		||||
		case bool:
 | 
			
		||||
			bl = append(bl, v)
 | 
			
		||||
		default:
 | 
			
		||||
			return n
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(s) == len(n) {
 | 
			
		||||
		return s
 | 
			
		||||
	} else if len(i64) == len(n) {
 | 
			
		||||
		return i64
 | 
			
		||||
	} else if len(f64) == len(n) {
 | 
			
		||||
		return f64
 | 
			
		||||
	} else if len(bl) == len(n) {
 | 
			
		||||
		return bl
 | 
			
		||||
	}
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPosition returns the position of the given key.
 | 
			
		||||
func (t *Tree) GetPosition(key string) Position {
 | 
			
		||||
	if key == "" {
 | 
			
		||||
		return t.position
 | 
			
		||||
	}
 | 
			
		||||
	return t.GetPositionPath(strings.Split(key, "."))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPositionPath sets the position of element in the tree indicated by 'keys'.
 | 
			
		||||
// If keys is of length zero, the current tree position is set.
 | 
			
		||||
func (t *Tree) SetPositionPath(keys []string, pos Position) {
 | 
			
		||||
	if len(keys) == 0 {
 | 
			
		||||
		t.position = pos
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	subtree := t
 | 
			
		||||
	for _, intermediateKey := range keys[:len(keys)-1] {
 | 
			
		||||
		value, exists := subtree.values[intermediateKey]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		switch node := value.(type) {
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			subtree = node
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			// go to most recent element
 | 
			
		||||
			if len(node) == 0 {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			subtree = node[len(node)-1]
 | 
			
		||||
		default:
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// branch based on final node type
 | 
			
		||||
	switch node := subtree.values[keys[len(keys)-1]].(type) {
 | 
			
		||||
	case *tomlValue:
 | 
			
		||||
		node.position = pos
 | 
			
		||||
		return
 | 
			
		||||
	case *Tree:
 | 
			
		||||
		node.position = pos
 | 
			
		||||
		return
 | 
			
		||||
	case []*Tree:
 | 
			
		||||
		// go to most recent element
 | 
			
		||||
		if len(node) == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		node[len(node)-1].position = pos
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPositionPath returns the element in the tree indicated by 'keys'.
 | 
			
		||||
// If keys is of length zero, the current tree is returned.
 | 
			
		||||
func (t *Tree) GetPositionPath(keys []string) Position {
 | 
			
		||||
	if len(keys) == 0 {
 | 
			
		||||
		return t.position
 | 
			
		||||
	}
 | 
			
		||||
	subtree := t
 | 
			
		||||
	for _, intermediateKey := range keys[:len(keys)-1] {
 | 
			
		||||
		value, exists := subtree.values[intermediateKey]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			return Position{0, 0}
 | 
			
		||||
		}
 | 
			
		||||
		switch node := value.(type) {
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			subtree = node
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			// go to most recent element
 | 
			
		||||
			if len(node) == 0 {
 | 
			
		||||
				return Position{0, 0}
 | 
			
		||||
			}
 | 
			
		||||
			subtree = node[len(node)-1]
 | 
			
		||||
		default:
 | 
			
		||||
			return Position{0, 0}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// branch based on final node type
 | 
			
		||||
	switch node := subtree.values[keys[len(keys)-1]].(type) {
 | 
			
		||||
	case *tomlValue:
 | 
			
		||||
		return node.position
 | 
			
		||||
	case *Tree:
 | 
			
		||||
		return node.position
 | 
			
		||||
	case []*Tree:
 | 
			
		||||
		// go to most recent element
 | 
			
		||||
		if len(node) == 0 {
 | 
			
		||||
			return Position{0, 0}
 | 
			
		||||
		}
 | 
			
		||||
		return node[len(node)-1].position
 | 
			
		||||
	default:
 | 
			
		||||
		return Position{0, 0}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDefault works like Get but with a default value
 | 
			
		||||
func (t *Tree) GetDefault(key string, def interface{}) interface{} {
 | 
			
		||||
	val := t.Get(key)
 | 
			
		||||
	if val == nil {
 | 
			
		||||
		return def
 | 
			
		||||
	}
 | 
			
		||||
	return val
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
 | 
			
		||||
// The default values within the struct are valid default options.
 | 
			
		||||
type SetOptions struct {
 | 
			
		||||
	Comment   string
 | 
			
		||||
	Commented bool
 | 
			
		||||
	Multiline bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetWithOptions is the same as Set, but allows you to provide formatting
 | 
			
		||||
// instructions to the key, that will be used by Marshal().
 | 
			
		||||
func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
 | 
			
		||||
	t.SetPathWithOptions(strings.Split(key, "."), opts, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPathWithOptions is the same as SetPath, but allows you to provide
 | 
			
		||||
// formatting instructions to the key, that will be reused by Marshal().
 | 
			
		||||
func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
 | 
			
		||||
	subtree := t
 | 
			
		||||
	for i, intermediateKey := range keys[:len(keys)-1] {
 | 
			
		||||
		nextTree, exists := subtree.values[intermediateKey]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
 | 
			
		||||
			subtree.values[intermediateKey] = nextTree // add new element here
 | 
			
		||||
		}
 | 
			
		||||
		switch node := nextTree.(type) {
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			subtree = node
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			// go to most recent element
 | 
			
		||||
			if len(node) == 0 {
 | 
			
		||||
				// create element if it does not exist
 | 
			
		||||
				node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
 | 
			
		||||
				subtree.values[intermediateKey] = node
 | 
			
		||||
			}
 | 
			
		||||
			subtree = node[len(node)-1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var toInsert interface{}
 | 
			
		||||
 | 
			
		||||
	switch v := value.(type) {
 | 
			
		||||
	case *Tree:
 | 
			
		||||
		v.comment = opts.Comment
 | 
			
		||||
		v.commented = opts.Commented
 | 
			
		||||
		toInsert = value
 | 
			
		||||
	case []*Tree:
 | 
			
		||||
		for i := range v {
 | 
			
		||||
			v[i].commented = opts.Commented
 | 
			
		||||
		}
 | 
			
		||||
		toInsert = value
 | 
			
		||||
	case *tomlValue:
 | 
			
		||||
		v.comment = opts.Comment
 | 
			
		||||
		v.commented = opts.Commented
 | 
			
		||||
		v.multiline = opts.Multiline
 | 
			
		||||
		toInsert = v
 | 
			
		||||
	default:
 | 
			
		||||
		toInsert = &tomlValue{value: value,
 | 
			
		||||
			comment:   opts.Comment,
 | 
			
		||||
			commented: opts.Commented,
 | 
			
		||||
			multiline: opts.Multiline,
 | 
			
		||||
			position:  Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subtree.values[keys[len(keys)-1]] = toInsert
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set an element in the tree.
 | 
			
		||||
// Key is a dot-separated path (e.g. a.b.c).
 | 
			
		||||
// Creates all necessary intermediate trees, if needed.
 | 
			
		||||
func (t *Tree) Set(key string, value interface{}) {
 | 
			
		||||
	t.SetWithComment(key, "", false, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetWithComment is the same as Set, but allows you to provide comment
 | 
			
		||||
// information to the key, that will be reused by Marshal().
 | 
			
		||||
func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
 | 
			
		||||
	t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPath sets an element in the tree.
 | 
			
		||||
// Keys is an array of path elements (e.g. {"a","b","c"}).
 | 
			
		||||
// Creates all necessary intermediate trees, if needed.
 | 
			
		||||
func (t *Tree) SetPath(keys []string, value interface{}) {
 | 
			
		||||
	t.SetPathWithComment(keys, "", false, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPathWithComment is the same as SetPath, but allows you to provide comment
 | 
			
		||||
// information to the key, that will be reused by Marshal().
 | 
			
		||||
func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
 | 
			
		||||
	t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete removes a key from the tree.
 | 
			
		||||
// Key is a dot-separated path (e.g. a.b.c).
 | 
			
		||||
func (t *Tree) Delete(key string) error {
 | 
			
		||||
	keys, err := parseKey(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return t.DeletePath(keys)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeletePath removes a key from the tree.
 | 
			
		||||
// Keys is an array of path elements (e.g. {"a","b","c"}).
 | 
			
		||||
func (t *Tree) DeletePath(keys []string) error {
 | 
			
		||||
	keyLen := len(keys)
 | 
			
		||||
	if keyLen == 1 {
 | 
			
		||||
		delete(t.values, keys[0])
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	tree := t.GetPath(keys[:keyLen-1])
 | 
			
		||||
	item := keys[keyLen-1]
 | 
			
		||||
	switch node := tree.(type) {
 | 
			
		||||
	case *Tree:
 | 
			
		||||
		delete(node.values, item)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New("no such key to delete")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// createSubTree takes a tree and a key and create the necessary intermediate
 | 
			
		||||
// subtrees to create a subtree at that point. In-place.
 | 
			
		||||
//
 | 
			
		||||
// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
 | 
			
		||||
// and tree[a][b][c]
 | 
			
		||||
//
 | 
			
		||||
// Returns nil on success, error object on failure
 | 
			
		||||
func (t *Tree) createSubTree(keys []string, pos Position) error {
 | 
			
		||||
	subtree := t
 | 
			
		||||
	for i, intermediateKey := range keys {
 | 
			
		||||
		nextTree, exists := subtree.values[intermediateKey]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
 | 
			
		||||
			tree.position = pos
 | 
			
		||||
			tree.inline = subtree.inline
 | 
			
		||||
			subtree.values[intermediateKey] = tree
 | 
			
		||||
			nextTree = tree
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch node := nextTree.(type) {
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			subtree = node[len(node)-1]
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			subtree = node
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
 | 
			
		||||
				strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadBytes creates a Tree from a []byte.
 | 
			
		||||
func LoadBytes(b []byte) (tree *Tree, err error) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			if _, ok := r.(runtime.Error); ok {
 | 
			
		||||
				panic(r)
 | 
			
		||||
			}
 | 
			
		||||
			err = errors.New(r.(string))
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
 | 
			
		||||
		b = b[4:]
 | 
			
		||||
	} else if len(b) >= 3 && hasUTF8BOM3(b) {
 | 
			
		||||
		b = b[3:]
 | 
			
		||||
	} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
 | 
			
		||||
		b = b[2:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tree = parseToml(lexToml(b))
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hasUTF16BigEndianBOM2(b []byte) bool {
 | 
			
		||||
	return b[0] == 0xFE && b[1] == 0xFF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hasUTF16LittleEndianBOM2(b []byte) bool {
 | 
			
		||||
	return b[0] == 0xFF && b[1] == 0xFE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hasUTF8BOM3(b []byte) bool {
 | 
			
		||||
	return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hasUTF32BigEndianBOM4(b []byte) bool {
 | 
			
		||||
	return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hasUTF32LittleEndianBOM4(b []byte) bool {
 | 
			
		||||
	return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadReader creates a Tree from any io.Reader.
 | 
			
		||||
func LoadReader(reader io.Reader) (tree *Tree, err error) {
 | 
			
		||||
	inputBytes, err := ioutil.ReadAll(reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	tree, err = LoadBytes(inputBytes)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load creates a Tree from a string.
 | 
			
		||||
func Load(content string) (tree *Tree, err error) {
 | 
			
		||||
	return LoadBytes([]byte(content))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadFile creates a Tree from a file.
 | 
			
		||||
func LoadFile(path string) (tree *Tree, err error) {
 | 
			
		||||
	file, err := os.Open(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
	return LoadReader(file)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										155
									
								
								vendor/github.com/pelletier/go-toml/tomltree_create.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								vendor/github.com/pelletier/go-toml/tomltree_create.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,155 @@
 | 
			
		|||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var kindToType = [reflect.String + 1]reflect.Type{
 | 
			
		||||
	reflect.Bool:    reflect.TypeOf(true),
 | 
			
		||||
	reflect.String:  reflect.TypeOf(""),
 | 
			
		||||
	reflect.Float32: reflect.TypeOf(float64(1)),
 | 
			
		||||
	reflect.Float64: reflect.TypeOf(float64(1)),
 | 
			
		||||
	reflect.Int:     reflect.TypeOf(int64(1)),
 | 
			
		||||
	reflect.Int8:    reflect.TypeOf(int64(1)),
 | 
			
		||||
	reflect.Int16:   reflect.TypeOf(int64(1)),
 | 
			
		||||
	reflect.Int32:   reflect.TypeOf(int64(1)),
 | 
			
		||||
	reflect.Int64:   reflect.TypeOf(int64(1)),
 | 
			
		||||
	reflect.Uint:    reflect.TypeOf(uint64(1)),
 | 
			
		||||
	reflect.Uint8:   reflect.TypeOf(uint64(1)),
 | 
			
		||||
	reflect.Uint16:  reflect.TypeOf(uint64(1)),
 | 
			
		||||
	reflect.Uint32:  reflect.TypeOf(uint64(1)),
 | 
			
		||||
	reflect.Uint64:  reflect.TypeOf(uint64(1)),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found.
 | 
			
		||||
// supported values:
 | 
			
		||||
// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32
 | 
			
		||||
func typeFor(k reflect.Kind) reflect.Type {
 | 
			
		||||
	if k > 0 && int(k) < len(kindToType) {
 | 
			
		||||
		return kindToType[k]
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func simpleValueCoercion(object interface{}) (interface{}, error) {
 | 
			
		||||
	switch original := object.(type) {
 | 
			
		||||
	case string, bool, int64, uint64, float64, time.Time:
 | 
			
		||||
		return original, nil
 | 
			
		||||
	case int:
 | 
			
		||||
		return int64(original), nil
 | 
			
		||||
	case int8:
 | 
			
		||||
		return int64(original), nil
 | 
			
		||||
	case int16:
 | 
			
		||||
		return int64(original), nil
 | 
			
		||||
	case int32:
 | 
			
		||||
		return int64(original), nil
 | 
			
		||||
	case uint:
 | 
			
		||||
		return uint64(original), nil
 | 
			
		||||
	case uint8:
 | 
			
		||||
		return uint64(original), nil
 | 
			
		||||
	case uint16:
 | 
			
		||||
		return uint64(original), nil
 | 
			
		||||
	case uint32:
 | 
			
		||||
		return uint64(original), nil
 | 
			
		||||
	case float32:
 | 
			
		||||
		return float64(original), nil
 | 
			
		||||
	case fmt.Stringer:
 | 
			
		||||
		return original.String(), nil
 | 
			
		||||
	case []interface{}:
 | 
			
		||||
		value := reflect.ValueOf(original)
 | 
			
		||||
		length := value.Len()
 | 
			
		||||
		arrayValue := reflect.MakeSlice(value.Type(), 0, length)
 | 
			
		||||
		for i := 0; i < length; i++ {
 | 
			
		||||
			val := value.Index(i).Interface()
 | 
			
		||||
			simpleValue, err := simpleValueCoercion(val)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
 | 
			
		||||
		}
 | 
			
		||||
		return arrayValue.Interface(), nil
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("cannot convert type %T to Tree", object)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sliceToTree(object interface{}) (interface{}, error) {
 | 
			
		||||
	// arrays are a bit tricky, since they can represent either a
 | 
			
		||||
	// collection of simple values, which is represented by one
 | 
			
		||||
	// *tomlValue, or an array of tables, which is represented by an
 | 
			
		||||
	// array of *Tree.
 | 
			
		||||
 | 
			
		||||
	// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice
 | 
			
		||||
	value := reflect.ValueOf(object)
 | 
			
		||||
	insideType := value.Type().Elem()
 | 
			
		||||
	length := value.Len()
 | 
			
		||||
	if length > 0 {
 | 
			
		||||
		insideType = reflect.ValueOf(value.Index(0).Interface()).Type()
 | 
			
		||||
	}
 | 
			
		||||
	if insideType.Kind() == reflect.Map {
 | 
			
		||||
		// this is considered as an array of tables
 | 
			
		||||
		tablesArray := make([]*Tree, 0, length)
 | 
			
		||||
		for i := 0; i < length; i++ {
 | 
			
		||||
			table := value.Index(i)
 | 
			
		||||
			tree, err := toTree(table.Interface())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			tablesArray = append(tablesArray, tree.(*Tree))
 | 
			
		||||
		}
 | 
			
		||||
		return tablesArray, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sliceType := typeFor(insideType.Kind())
 | 
			
		||||
	if sliceType == nil {
 | 
			
		||||
		sliceType = insideType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < length; i++ {
 | 
			
		||||
		val := value.Index(i).Interface()
 | 
			
		||||
		simpleValue, err := simpleValueCoercion(val)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
 | 
			
		||||
	}
 | 
			
		||||
	return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toTree(object interface{}) (interface{}, error) {
 | 
			
		||||
	value := reflect.ValueOf(object)
 | 
			
		||||
 | 
			
		||||
	if value.Kind() == reflect.Map {
 | 
			
		||||
		values := map[string]interface{}{}
 | 
			
		||||
		keys := value.MapKeys()
 | 
			
		||||
		for _, key := range keys {
 | 
			
		||||
			if key.Kind() != reflect.String {
 | 
			
		||||
				if _, ok := key.Interface().(string); !ok {
 | 
			
		||||
					return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind())
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			v := value.MapIndex(key)
 | 
			
		||||
			newValue, err := toTree(v.Interface())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			values[key.String()] = newValue
 | 
			
		||||
		}
 | 
			
		||||
		return &Tree{values: values, position: Position{}}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if value.Kind() == reflect.Array || value.Kind() == reflect.Slice {
 | 
			
		||||
		return sliceToTree(object)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	simpleValue, err := simpleValueCoercion(object)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &tomlValue{value: simpleValue, position: Position{}}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										517
									
								
								vendor/github.com/pelletier/go-toml/tomltree_write.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										517
									
								
								vendor/github.com/pelletier/go-toml/tomltree_write.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,517 @@
 | 
			
		|||
package toml
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type valueComplexity int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	valueSimple valueComplexity = iota + 1
 | 
			
		||||
	valueComplex
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type sortNode struct {
 | 
			
		||||
	key        string
 | 
			
		||||
	complexity valueComplexity
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encodes a string to a TOML-compliant multi-line string value
 | 
			
		||||
// This function is a clone of the existing encodeTomlString function, except that whitespace characters
 | 
			
		||||
// are preserved. Quotation marks and backslashes are also not escaped.
 | 
			
		||||
func encodeMultilineTomlString(value string, commented string) string {
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
	adjacentQuoteCount := 0
 | 
			
		||||
 | 
			
		||||
	b.WriteString(commented)
 | 
			
		||||
	for i, rr := range value {
 | 
			
		||||
		if rr != '"' {
 | 
			
		||||
			adjacentQuoteCount = 0
 | 
			
		||||
		} else {
 | 
			
		||||
			adjacentQuoteCount++
 | 
			
		||||
		}
 | 
			
		||||
		switch rr {
 | 
			
		||||
		case '\b':
 | 
			
		||||
			b.WriteString(`\b`)
 | 
			
		||||
		case '\t':
 | 
			
		||||
			b.WriteString("\t")
 | 
			
		||||
		case '\n':
 | 
			
		||||
			b.WriteString("\n" + commented)
 | 
			
		||||
		case '\f':
 | 
			
		||||
			b.WriteString(`\f`)
 | 
			
		||||
		case '\r':
 | 
			
		||||
			b.WriteString("\r")
 | 
			
		||||
		case '"':
 | 
			
		||||
			if adjacentQuoteCount >= 3 || i == len(value)-1 {
 | 
			
		||||
				adjacentQuoteCount = 0
 | 
			
		||||
				b.WriteString(`\"`)
 | 
			
		||||
			} else {
 | 
			
		||||
				b.WriteString(`"`)
 | 
			
		||||
			}
 | 
			
		||||
		case '\\':
 | 
			
		||||
			b.WriteString(`\`)
 | 
			
		||||
		default:
 | 
			
		||||
			intRr := uint16(rr)
 | 
			
		||||
			if intRr < 0x001F {
 | 
			
		||||
				b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
 | 
			
		||||
			} else {
 | 
			
		||||
				b.WriteRune(rr)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return b.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encodes a string to a TOML-compliant string value
 | 
			
		||||
func encodeTomlString(value string) string {
 | 
			
		||||
	var b bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	for _, rr := range value {
 | 
			
		||||
		switch rr {
 | 
			
		||||
		case '\b':
 | 
			
		||||
			b.WriteString(`\b`)
 | 
			
		||||
		case '\t':
 | 
			
		||||
			b.WriteString(`\t`)
 | 
			
		||||
		case '\n':
 | 
			
		||||
			b.WriteString(`\n`)
 | 
			
		||||
		case '\f':
 | 
			
		||||
			b.WriteString(`\f`)
 | 
			
		||||
		case '\r':
 | 
			
		||||
			b.WriteString(`\r`)
 | 
			
		||||
		case '"':
 | 
			
		||||
			b.WriteString(`\"`)
 | 
			
		||||
		case '\\':
 | 
			
		||||
			b.WriteString(`\\`)
 | 
			
		||||
		default:
 | 
			
		||||
			intRr := uint16(rr)
 | 
			
		||||
			if intRr < 0x001F {
 | 
			
		||||
				b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
 | 
			
		||||
			} else {
 | 
			
		||||
				b.WriteRune(rr)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return b.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tomlTreeStringRepresentation(t *Tree, ord marshalOrder) (string, error) {
 | 
			
		||||
	var orderedVals []sortNode
 | 
			
		||||
	switch ord {
 | 
			
		||||
	case OrderPreserve:
 | 
			
		||||
		orderedVals = sortByLines(t)
 | 
			
		||||
	default:
 | 
			
		||||
		orderedVals = sortAlphabetical(t)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var values []string
 | 
			
		||||
	for _, node := range orderedVals {
 | 
			
		||||
		k := node.key
 | 
			
		||||
		v := t.values[k]
 | 
			
		||||
 | 
			
		||||
		repr, err := tomlValueStringRepresentation(v, "", "", ord, false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		values = append(values, quoteKeyIfNeeded(k)+" = "+repr)
 | 
			
		||||
	}
 | 
			
		||||
	return "{ " + strings.Join(values, ", ") + " }", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tomlValueStringRepresentation(v interface{}, commented string, indent string, ord marshalOrder, arraysOneElementPerLine bool) (string, error) {
 | 
			
		||||
	// this interface check is added to dereference the change made in the writeTo function.
 | 
			
		||||
	// That change was made to allow this function to see formatting options.
 | 
			
		||||
	tv, ok := v.(*tomlValue)
 | 
			
		||||
	if ok {
 | 
			
		||||
		v = tv.value
 | 
			
		||||
	} else {
 | 
			
		||||
		tv = &tomlValue{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch value := v.(type) {
 | 
			
		||||
	case uint64:
 | 
			
		||||
		return strconv.FormatUint(value, 10), nil
 | 
			
		||||
	case int64:
 | 
			
		||||
		return strconv.FormatInt(value, 10), nil
 | 
			
		||||
	case float64:
 | 
			
		||||
		// Default bit length is full 64
 | 
			
		||||
		bits := 64
 | 
			
		||||
		// Float panics if nan is used
 | 
			
		||||
		if !math.IsNaN(value) {
 | 
			
		||||
			// if 32 bit accuracy is enough to exactly show, use 32
 | 
			
		||||
			_, acc := big.NewFloat(value).Float32()
 | 
			
		||||
			if acc == big.Exact {
 | 
			
		||||
				bits = 32
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if math.Trunc(value) == value {
 | 
			
		||||
			return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil
 | 
			
		||||
		}
 | 
			
		||||
		return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil
 | 
			
		||||
	case string:
 | 
			
		||||
		if tv.multiline {
 | 
			
		||||
			return "\"\"\"\n" + encodeMultilineTomlString(value, commented) + "\"\"\"", nil
 | 
			
		||||
		}
 | 
			
		||||
		return "\"" + encodeTomlString(value) + "\"", nil
 | 
			
		||||
	case []byte:
 | 
			
		||||
		b, _ := v.([]byte)
 | 
			
		||||
		return string(b), nil
 | 
			
		||||
	case bool:
 | 
			
		||||
		if value {
 | 
			
		||||
			return "true", nil
 | 
			
		||||
		}
 | 
			
		||||
		return "false", nil
 | 
			
		||||
	case time.Time:
 | 
			
		||||
		return value.Format(time.RFC3339), nil
 | 
			
		||||
	case LocalDate:
 | 
			
		||||
		return value.String(), nil
 | 
			
		||||
	case LocalDateTime:
 | 
			
		||||
		return value.String(), nil
 | 
			
		||||
	case LocalTime:
 | 
			
		||||
		return value.String(), nil
 | 
			
		||||
	case *Tree:
 | 
			
		||||
		return tomlTreeStringRepresentation(value, ord)
 | 
			
		||||
	case nil:
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rv := reflect.ValueOf(v)
 | 
			
		||||
 | 
			
		||||
	if rv.Kind() == reflect.Slice {
 | 
			
		||||
		var values []string
 | 
			
		||||
		for i := 0; i < rv.Len(); i++ {
 | 
			
		||||
			item := rv.Index(i).Interface()
 | 
			
		||||
			itemRepr, err := tomlValueStringRepresentation(item, commented, indent, ord, arraysOneElementPerLine)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
			values = append(values, itemRepr)
 | 
			
		||||
		}
 | 
			
		||||
		if arraysOneElementPerLine && len(values) > 1 {
 | 
			
		||||
			stringBuffer := bytes.Buffer{}
 | 
			
		||||
			valueIndent := indent + `  ` // TODO: move that to a shared encoder state
 | 
			
		||||
 | 
			
		||||
			stringBuffer.WriteString("[\n")
 | 
			
		||||
 | 
			
		||||
			for _, value := range values {
 | 
			
		||||
				stringBuffer.WriteString(valueIndent)
 | 
			
		||||
				stringBuffer.WriteString(commented + value)
 | 
			
		||||
				stringBuffer.WriteString(`,`)
 | 
			
		||||
				stringBuffer.WriteString("\n")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stringBuffer.WriteString(indent + commented + "]")
 | 
			
		||||
 | 
			
		||||
			return stringBuffer.String(), nil
 | 
			
		||||
		}
 | 
			
		||||
		return "[" + strings.Join(values, ", ") + "]", nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", fmt.Errorf("unsupported value type %T: %v", v, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getTreeArrayLine(trees []*Tree) (line int) {
 | 
			
		||||
	// get lowest line number that is not 0
 | 
			
		||||
	for _, tv := range trees {
 | 
			
		||||
		if tv.position.Line < line || line == 0 {
 | 
			
		||||
			line = tv.position.Line
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sortByLines(t *Tree) (vals []sortNode) {
 | 
			
		||||
	var (
 | 
			
		||||
		line  int
 | 
			
		||||
		lines []int
 | 
			
		||||
		tv    *Tree
 | 
			
		||||
		tom   *tomlValue
 | 
			
		||||
		node  sortNode
 | 
			
		||||
	)
 | 
			
		||||
	vals = make([]sortNode, 0)
 | 
			
		||||
	m := make(map[int]sortNode)
 | 
			
		||||
 | 
			
		||||
	for k := range t.values {
 | 
			
		||||
		v := t.values[k]
 | 
			
		||||
		switch v.(type) {
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			tv = v.(*Tree)
 | 
			
		||||
			line = tv.position.Line
 | 
			
		||||
			node = sortNode{key: k, complexity: valueComplex}
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			line = getTreeArrayLine(v.([]*Tree))
 | 
			
		||||
			node = sortNode{key: k, complexity: valueComplex}
 | 
			
		||||
		default:
 | 
			
		||||
			tom = v.(*tomlValue)
 | 
			
		||||
			line = tom.position.Line
 | 
			
		||||
			node = sortNode{key: k, complexity: valueSimple}
 | 
			
		||||
		}
 | 
			
		||||
		lines = append(lines, line)
 | 
			
		||||
		vals = append(vals, node)
 | 
			
		||||
		m[line] = node
 | 
			
		||||
	}
 | 
			
		||||
	sort.Ints(lines)
 | 
			
		||||
 | 
			
		||||
	for i, line := range lines {
 | 
			
		||||
		vals[i] = m[line]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sortAlphabetical(t *Tree) (vals []sortNode) {
 | 
			
		||||
	var (
 | 
			
		||||
		node     sortNode
 | 
			
		||||
		simpVals []string
 | 
			
		||||
		compVals []string
 | 
			
		||||
	)
 | 
			
		||||
	vals = make([]sortNode, 0)
 | 
			
		||||
	m := make(map[string]sortNode)
 | 
			
		||||
 | 
			
		||||
	for k := range t.values {
 | 
			
		||||
		v := t.values[k]
 | 
			
		||||
		switch v.(type) {
 | 
			
		||||
		case *Tree, []*Tree:
 | 
			
		||||
			node = sortNode{key: k, complexity: valueComplex}
 | 
			
		||||
			compVals = append(compVals, node.key)
 | 
			
		||||
		default:
 | 
			
		||||
			node = sortNode{key: k, complexity: valueSimple}
 | 
			
		||||
			simpVals = append(simpVals, node.key)
 | 
			
		||||
		}
 | 
			
		||||
		vals = append(vals, node)
 | 
			
		||||
		m[node.key] = node
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Simples first to match previous implementation
 | 
			
		||||
	sort.Strings(simpVals)
 | 
			
		||||
	i := 0
 | 
			
		||||
	for _, key := range simpVals {
 | 
			
		||||
		vals[i] = m[key]
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(compVals)
 | 
			
		||||
	for _, key := range compVals {
 | 
			
		||||
		vals[i] = m[key]
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return vals
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
 | 
			
		||||
	return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical, "  ", false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder, indentString string, parentCommented bool) (int64, error) {
 | 
			
		||||
	var orderedVals []sortNode
 | 
			
		||||
 | 
			
		||||
	switch ord {
 | 
			
		||||
	case OrderPreserve:
 | 
			
		||||
		orderedVals = sortByLines(t)
 | 
			
		||||
	default:
 | 
			
		||||
		orderedVals = sortAlphabetical(t)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, node := range orderedVals {
 | 
			
		||||
		switch node.complexity {
 | 
			
		||||
		case valueComplex:
 | 
			
		||||
			k := node.key
 | 
			
		||||
			v := t.values[k]
 | 
			
		||||
 | 
			
		||||
			combinedKey := quoteKeyIfNeeded(k)
 | 
			
		||||
			if keyspace != "" {
 | 
			
		||||
				combinedKey = keyspace + "." + combinedKey
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch node := v.(type) {
 | 
			
		||||
			// node has to be of those two types given how keys are sorted above
 | 
			
		||||
			case *Tree:
 | 
			
		||||
				tv, ok := t.values[k].(*Tree)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
 | 
			
		||||
				}
 | 
			
		||||
				if tv.comment != "" {
 | 
			
		||||
					comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
 | 
			
		||||
					start := "# "
 | 
			
		||||
					if strings.HasPrefix(comment, "#") {
 | 
			
		||||
						start = ""
 | 
			
		||||
					}
 | 
			
		||||
					writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
 | 
			
		||||
					bytesCount += int64(writtenBytesCountComment)
 | 
			
		||||
					if errc != nil {
 | 
			
		||||
						return bytesCount, errc
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				var commented string
 | 
			
		||||
				if parentCommented || t.commented || tv.commented {
 | 
			
		||||
					commented = "# "
 | 
			
		||||
				}
 | 
			
		||||
				writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
 | 
			
		||||
				bytesCount += int64(writtenBytesCount)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return bytesCount, err
 | 
			
		||||
				}
 | 
			
		||||
				bytesCount, err = node.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, parentCommented || t.commented || tv.commented)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return bytesCount, err
 | 
			
		||||
				}
 | 
			
		||||
			case []*Tree:
 | 
			
		||||
				for _, subTree := range node {
 | 
			
		||||
					var commented string
 | 
			
		||||
					if parentCommented || t.commented || subTree.commented {
 | 
			
		||||
						commented = "# "
 | 
			
		||||
					}
 | 
			
		||||
					writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
 | 
			
		||||
					bytesCount += int64(writtenBytesCount)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return bytesCount, err
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					bytesCount, err = subTree.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, parentCommented || t.commented || subTree.commented)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return bytesCount, err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		default: // Simple
 | 
			
		||||
			k := node.key
 | 
			
		||||
			v, ok := t.values[k].(*tomlValue)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var commented string
 | 
			
		||||
			if parentCommented || t.commented || v.commented {
 | 
			
		||||
				commented = "# "
 | 
			
		||||
			}
 | 
			
		||||
			repr, err := tomlValueStringRepresentation(v, commented, indent, ord, arraysOneElementPerLine)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return bytesCount, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if v.comment != "" {
 | 
			
		||||
				comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1)
 | 
			
		||||
				start := "# "
 | 
			
		||||
				if strings.HasPrefix(comment, "#") {
 | 
			
		||||
					start = ""
 | 
			
		||||
				}
 | 
			
		||||
				writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n")
 | 
			
		||||
				bytesCount += int64(writtenBytesCountComment)
 | 
			
		||||
				if errc != nil {
 | 
			
		||||
					return bytesCount, errc
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			quotedKey := quoteKeyIfNeeded(k)
 | 
			
		||||
			writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n")
 | 
			
		||||
			bytesCount += int64(writtenBytesCount)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return bytesCount, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bytesCount, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// quote a key if it does not fit the bare key format (A-Za-z0-9_-)
 | 
			
		||||
// quoted keys use the same rules as strings
 | 
			
		||||
func quoteKeyIfNeeded(k string) string {
 | 
			
		||||
	// when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain
 | 
			
		||||
	// keys that have already been quoted.
 | 
			
		||||
	// not an ideal situation, but good enough of a stop gap.
 | 
			
		||||
	if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' {
 | 
			
		||||
		return k
 | 
			
		||||
	}
 | 
			
		||||
	isBare := true
 | 
			
		||||
	for _, r := range k {
 | 
			
		||||
		if !isValidBareChar(r) {
 | 
			
		||||
			isBare = false
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if isBare {
 | 
			
		||||
		return k
 | 
			
		||||
	}
 | 
			
		||||
	return quoteKey(k)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func quoteKey(k string) string {
 | 
			
		||||
	return "\"" + encodeTomlString(k) + "\""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeStrings(w io.Writer, s ...string) (int, error) {
 | 
			
		||||
	var n int
 | 
			
		||||
	for i := range s {
 | 
			
		||||
		b, err := io.WriteString(w, s[i])
 | 
			
		||||
		n += b
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return n, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteTo encode the Tree as Toml and writes it to the writer w.
 | 
			
		||||
// Returns the number of bytes written in case of success, or an error if anything happened.
 | 
			
		||||
func (t *Tree) WriteTo(w io.Writer) (int64, error) {
 | 
			
		||||
	return t.writeTo(w, "", "", 0, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToTomlString generates a human-readable representation of the current tree.
 | 
			
		||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser.
 | 
			
		||||
// If the conversion cannot be performed, ToString returns a non-nil error.
 | 
			
		||||
func (t *Tree) ToTomlString() (string, error) {
 | 
			
		||||
	b, err := t.Marshal()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return string(b), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String generates a human-readable representation of the current tree.
 | 
			
		||||
// Alias of ToString. Present to implement the fmt.Stringer interface.
 | 
			
		||||
func (t *Tree) String() string {
 | 
			
		||||
	result, _ := t.ToTomlString()
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ToMap recursively generates a representation of the tree using Go built-in structures.
 | 
			
		||||
// The following types are used:
 | 
			
		||||
//
 | 
			
		||||
//	* bool
 | 
			
		||||
//	* float64
 | 
			
		||||
//	* int64
 | 
			
		||||
//	* string
 | 
			
		||||
//	* uint64
 | 
			
		||||
//	* time.Time
 | 
			
		||||
//	* map[string]interface{} (where interface{} is any of this list)
 | 
			
		||||
//	* []interface{} (where interface{} is any of this list)
 | 
			
		||||
func (t *Tree) ToMap() map[string]interface{} {
 | 
			
		||||
	result := map[string]interface{}{}
 | 
			
		||||
 | 
			
		||||
	for k, v := range t.values {
 | 
			
		||||
		switch node := v.(type) {
 | 
			
		||||
		case []*Tree:
 | 
			
		||||
			var array []interface{}
 | 
			
		||||
			for _, item := range node {
 | 
			
		||||
				array = append(array, item.ToMap())
 | 
			
		||||
			}
 | 
			
		||||
			result[k] = array
 | 
			
		||||
		case *Tree:
 | 
			
		||||
			result[k] = node.ToMap()
 | 
			
		||||
		case *tomlValue:
 | 
			
		||||
			result[k] = node.value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue