forked from forgejo/forgejo
Use vendored go-swagger (#8087)
* Use vendored go-swagger * vendor go-swagger * revert un wanteed change * remove un-needed GO111MODULE * Update Makefile Co-Authored-By: techknowlogick <matti@mdranta.net>
This commit is contained in:
parent
4cb1bdddc8
commit
9fe4437bda
686 changed files with 143379 additions and 17 deletions
2
vendor/github.com/pelletier/go-toml/.dockerignore
generated
vendored
Normal file
2
vendor/github.com/pelletier/go-toml/.dockerignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
cmd/tomll/tomll
|
||||
cmd/tomljson/tomljson
|
5
vendor/github.com/pelletier/go-toml/.gitignore
generated
vendored
Normal file
5
vendor/github.com/pelletier/go-toml/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
test_program/test_program_bin
|
||||
fuzz/
|
||||
cmd/tomll/tomll
|
||||
cmd/tomljson/tomljson
|
||||
cmd/tomltestgen/tomltestgen
|
22
vendor/github.com/pelletier/go-toml/.travis.yml
generated
vendored
Normal file
22
vendor/github.com/pelletier/go-toml/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- tip
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
fast_finish: true
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
script:
|
||||
- if [ -n "$(go fmt ./...)" ]; then exit 1; fi
|
||||
- go test github.com/pelletier/go-toml -race -coverprofile=coverage.txt -covermode=atomic
|
||||
- go test github.com/pelletier/go-toml/cmd/tomljson
|
||||
- go test github.com/pelletier/go-toml/cmd/tomll
|
||||
- go test github.com/pelletier/go-toml/query
|
||||
- ./benchmark.sh $TRAVIS_BRANCH https://github.com/$TRAVIS_REPO_SLUG.git
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
132
vendor/github.com/pelletier/go-toml/CONTRIBUTING.md
generated
vendored
Normal file
132
vendor/github.com/pelletier/go-toml/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
## Contributing
|
||||
|
||||
Thank you for your interest in go-toml! We appreciate you considering
|
||||
contributing to go-toml!
|
||||
|
||||
The main goal is the project is to provide an easy-to-use TOML
|
||||
implementation for Go that gets the job done and gets out of your way –
|
||||
dealing with TOML is probably not the central piece of your project.
|
||||
|
||||
As the single maintainer of go-toml, time is scarce. All help, big or
|
||||
small, is more than welcomed!
|
||||
|
||||
### Ask questions
|
||||
|
||||
Any question you may have, somebody else might have it too. Always feel
|
||||
free to ask them on the [issues tracker][issues-tracker]. We will try to
|
||||
answer them as clearly and quickly as possible, time permitting.
|
||||
|
||||
Asking questions also helps us identify areas where the documentation needs
|
||||
improvement, or new features that weren't envisioned before. Sometimes, a
|
||||
seemingly innocent question leads to the fix of a bug. Don't hesitate and
|
||||
ask away!
|
||||
|
||||
### Improve the documentation
|
||||
|
||||
The best way to share your knowledge and experience with go-toml is to
|
||||
improve the documentation. Fix a typo, clarify an interface, add an
|
||||
example, anything goes!
|
||||
|
||||
The documentation is present in the [README][readme] and thorough the
|
||||
source code. On release, it gets updated on [GoDoc][godoc]. To make a
|
||||
change to the documentation, create a pull request with your proposed
|
||||
changes. For simple changes like that, the easiest way to go is probably
|
||||
the "Fork this project and edit the file" button on Github, displayed at
|
||||
the top right of the file. Unless it's a trivial change (for example a
|
||||
typo), provide a little bit of context in your pull request description or
|
||||
commit message.
|
||||
|
||||
### Report a bug
|
||||
|
||||
Found a bug! Sorry to hear that :(. Help us and other track them down and
|
||||
fix by reporting it. [File a new bug report][bug-report] on the [issues
|
||||
tracker][issues-tracker]. The template should provide enough guidance on
|
||||
what to include. When in doubt: add more details! By reducing ambiguity and
|
||||
providing more information, it decreases back and forth and saves everyone
|
||||
time.
|
||||
|
||||
### Code changes
|
||||
|
||||
Want to contribute a patch? Very happy to hear that!
|
||||
|
||||
First, some high-level rules:
|
||||
|
||||
* A short proposal with some POC code is better than a lengthy piece of
|
||||
text with no code. Code speaks louder than words.
|
||||
* No backward-incompatible patch will be accepted unless discussed.
|
||||
Sometimes it's hard, and Go's lack of versioning by default does not
|
||||
help, but we try not to break people's programs unless we absolutely have
|
||||
to.
|
||||
* If you are writing a new feature or extending an existing one, make sure
|
||||
to write some documentation.
|
||||
* Bug fixes need to be accompanied with regression tests.
|
||||
* New code needs to be tested.
|
||||
* Your commit messages need to explain why the change is needed, even if
|
||||
already included in the PR description.
|
||||
|
||||
It does sound like a lot, but those best practices are here to save time
|
||||
overall and continuously improve the quality of the project, which is
|
||||
something everyone benefits from.
|
||||
|
||||
#### Get started
|
||||
|
||||
The fairly standard code contribution process looks like that:
|
||||
|
||||
1. [Fork the project][fork].
|
||||
2. Make your changes, commit on any branch you like.
|
||||
3. [Open up a pull request][pull-request]
|
||||
4. Review, potential ask for changes.
|
||||
5. Merge. You're in!
|
||||
|
||||
Feel free to ask for help! You can create draft pull requests to gather
|
||||
some early feedback!
|
||||
|
||||
#### Run the tests
|
||||
|
||||
You can run tests for go-toml using Go's test tool: `go test ./...`.
|
||||
When creating a pull requests, all tests will be ran on Linux on a few Go
|
||||
versions (Travis CI), and on Windows using the latest Go version
|
||||
(AppVeyor).
|
||||
|
||||
#### Style
|
||||
|
||||
Try to look around and follow the same format and structure as the rest of
|
||||
the code. We enforce using `go fmt` on the whole code base.
|
||||
|
||||
---
|
||||
|
||||
### Maintainers-only
|
||||
|
||||
#### Merge pull request
|
||||
|
||||
Checklist:
|
||||
|
||||
* Passing CI.
|
||||
* Does not introduce backward-incompatible changes (unless discussed).
|
||||
* Has relevant doc changes.
|
||||
* Has relevant unit tests.
|
||||
|
||||
1. Merge using "squash and merge".
|
||||
2. Make sure to edit the commit message to keep all the useful information
|
||||
nice and clean.
|
||||
3. Make sure the commit title is clear and contains the PR number (#123).
|
||||
|
||||
#### New release
|
||||
|
||||
1. Go to [releases][releases]. Click on "X commits to master since this
|
||||
release".
|
||||
2. Make note of all the changes. Look for backward incompatible changes,
|
||||
new features, and bug fixes.
|
||||
3. Pick the new version using the above and semver.
|
||||
4. Create a [new release][new-release].
|
||||
5. Follow the same format as [1.1.0][release-110].
|
||||
|
||||
[issues-tracker]: https://github.com/pelletier/go-toml/issues
|
||||
[bug-report]: https://github.com/pelletier/go-toml/issues/new?template=bug_report.md
|
||||
[godoc]: https://godoc.org/github.com/pelletier/go-toml
|
||||
[readme]: ./README.md
|
||||
[fork]: https://help.github.com/articles/fork-a-repo
|
||||
[pull-request]: https://help.github.com/en/articles/creating-a-pull-request
|
||||
[releases]: https://github.com/pelletier/go-toml/releases
|
||||
[new-release]: https://github.com/pelletier/go-toml/releases/new
|
||||
[release-110]: https://github.com/pelletier/go-toml/releases/tag/v1.1.0
|
10
vendor/github.com/pelletier/go-toml/Dockerfile
generated
vendored
Normal file
10
vendor/github.com/pelletier/go-toml/Dockerfile
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
FROM golang:1.12-alpine3.9 as builder
|
||||
WORKDIR /go/src/github.com/pelletier/go-toml
|
||||
COPY . .
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOOS=linux
|
||||
RUN go install ./...
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /go/bin/tomll /usr/bin/tomll
|
||||
COPY --from=builder /go/bin/tomljson /usr/bin/tomljson
|
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.
|
5
vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
5
vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
**Issue:** add link to pelletier/go-toml issue here
|
||||
|
||||
Explanation of what this pull request does.
|
||||
|
||||
More detailed description of the decisions being made and the reasons why (if the patch is non-trivial).
|
145
vendor/github.com/pelletier/go-toml/README.md
generated
vendored
Normal file
145
vendor/github.com/pelletier/go-toml/README.md
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
# go-toml
|
||||
|
||||
Go library for the [TOML](https://github.com/mojombo/toml) format.
|
||||
|
||||
This library supports TOML version
|
||||
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
||||
|
||||
[](http://godoc.org/github.com/pelletier/go-toml)
|
||||
[](https://github.com/pelletier/go-toml/blob/master/LICENSE)
|
||||
[](https://travis-ci.org/pelletier/go-toml)
|
||||
[](https://ci.appveyor.com/project/pelletier/go-toml/branch/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
|
||||
* Mashaling 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.Println("Query result %d: %v", 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 lint 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
|
||||
```
|
||||
|
||||
### 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).
|
34
vendor/github.com/pelletier/go-toml/appveyor.yml
generated
vendored
Normal file
34
vendor/github.com/pelletier/go-toml/appveyor.yml
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
version: "{build}"
|
||||
|
||||
# Source Config
|
||||
clone_folder: c:\gopath\src\github.com\pelletier\go-toml
|
||||
|
||||
# Build host
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
DEPTESTBYPASS501: 1
|
||||
GOVERSION: 1.12
|
||||
GO111MODULE: on
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
# Build
|
||||
install:
|
||||
# Install the specific Go version.
|
||||
- rmdir c:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi
|
||||
- msiexec /i go%GOVERSION%.windows-amd64.msi /q
|
||||
- choco install bzr
|
||||
- set Path=c:\go\bin;c:\gopath\bin;C:\Program Files (x86)\Bazaar\;C:\Program Files\Mercurial\%Path%
|
||||
- go version
|
||||
- go env
|
||||
|
||||
build: false
|
||||
deploy: false
|
||||
|
||||
test_script:
|
||||
- go test github.com/pelletier/go-toml
|
||||
- go test github.com/pelletier/go-toml/cmd/tomljson
|
||||
- go test github.com/pelletier/go-toml/cmd/tomll
|
||||
- go test github.com/pelletier/go-toml/query
|
164
vendor/github.com/pelletier/go-toml/benchmark.json
generated
vendored
Normal file
164
vendor/github.com/pelletier/go-toml/benchmark.json
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
{
|
||||
"array": {
|
||||
"key1": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"key2": [
|
||||
"red",
|
||||
"yellow",
|
||||
"green"
|
||||
],
|
||||
"key3": [
|
||||
[
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
],
|
||||
"key4": [
|
||||
[
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
],
|
||||
"key5": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"key6": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
"boolean": {
|
||||
"False": false,
|
||||
"True": true
|
||||
},
|
||||
"datetime": {
|
||||
"key1": "1979-05-27T07:32:00Z",
|
||||
"key2": "1979-05-27T00:32:00-07:00",
|
||||
"key3": "1979-05-27T00:32:00.999999-07:00"
|
||||
},
|
||||
"float": {
|
||||
"both": {
|
||||
"key": 6.626e-34
|
||||
},
|
||||
"exponent": {
|
||||
"key1": 5e+22,
|
||||
"key2": 1000000,
|
||||
"key3": -0.02
|
||||
},
|
||||
"fractional": {
|
||||
"key1": 1,
|
||||
"key2": 3.1415,
|
||||
"key3": -0.01
|
||||
},
|
||||
"underscores": {
|
||||
"key1": 9224617.445991227,
|
||||
"key2": 1e+100
|
||||
}
|
||||
},
|
||||
"fruit": [{
|
||||
"name": "apple",
|
||||
"physical": {
|
||||
"color": "red",
|
||||
"shape": "round"
|
||||
},
|
||||
"variety": [{
|
||||
"name": "red delicious"
|
||||
},
|
||||
{
|
||||
"name": "granny smith"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "banana",
|
||||
"variety": [{
|
||||
"name": "plantain"
|
||||
}]
|
||||
}
|
||||
],
|
||||
"integer": {
|
||||
"key1": 99,
|
||||
"key2": 42,
|
||||
"key3": 0,
|
||||
"key4": -17,
|
||||
"underscores": {
|
||||
"key1": 1000,
|
||||
"key2": 5349221,
|
||||
"key3": 12345
|
||||
}
|
||||
},
|
||||
"products": [{
|
||||
"name": "Hammer",
|
||||
"sku": 738594937
|
||||
},
|
||||
{},
|
||||
{
|
||||
"color": "gray",
|
||||
"name": "Nail",
|
||||
"sku": 284758393
|
||||
}
|
||||
],
|
||||
"string": {
|
||||
"basic": {
|
||||
"basic": "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
|
||||
},
|
||||
"literal": {
|
||||
"multiline": {
|
||||
"lines": "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n",
|
||||
"regex2": "I [dw]on't need \\d{2} apples"
|
||||
},
|
||||
"quoted": "Tom \"Dubs\" Preston-Werner",
|
||||
"regex": "\u003c\\i\\c*\\s*\u003e",
|
||||
"winpath": "C:\\Users\\nodejs\\templates",
|
||||
"winpath2": "\\\\ServerX\\admin$\\system32\\"
|
||||
},
|
||||
"multiline": {
|
||||
"continued": {
|
||||
"key1": "The quick brown fox jumps over the lazy dog.",
|
||||
"key2": "The quick brown fox jumps over the lazy dog.",
|
||||
"key3": "The quick brown fox jumps over the lazy dog."
|
||||
},
|
||||
"key1": "One\nTwo",
|
||||
"key2": "One\nTwo",
|
||||
"key3": "One\nTwo"
|
||||
}
|
||||
},
|
||||
"table": {
|
||||
"inline": {
|
||||
"name": {
|
||||
"first": "Tom",
|
||||
"last": "Preston-Werner"
|
||||
},
|
||||
"point": {
|
||||
"x": 1,
|
||||
"y": 2
|
||||
}
|
||||
},
|
||||
"key": "value",
|
||||
"subtable": {
|
||||
"key": "another value"
|
||||
}
|
||||
},
|
||||
"x": {
|
||||
"y": {
|
||||
"z": {
|
||||
"w": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
vendor/github.com/pelletier/go-toml/benchmark.sh
generated
vendored
Normal file
32
vendor/github.com/pelletier/go-toml/benchmark.sh
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
reference_ref=${1:-master}
|
||||
reference_git=${2:-.}
|
||||
|
||||
if ! `hash benchstat 2>/dev/null`; then
|
||||
echo "Installing benchstat"
|
||||
go get golang.org/x/perf/cmd/benchstat
|
||||
go install golang.org/x/perf/cmd/benchstat
|
||||
fi
|
||||
|
||||
tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX`
|
||||
ref_tempdir="${tempdir}/ref"
|
||||
ref_benchmark="${ref_tempdir}/benchmark-`echo -n ${reference_ref}|tr -s '/' '-'`.txt"
|
||||
local_benchmark="`pwd`/benchmark-local.txt"
|
||||
|
||||
echo "=== ${reference_ref} (${ref_tempdir})"
|
||||
git clone ${reference_git} ${ref_tempdir} >/dev/null 2>/dev/null
|
||||
pushd ${ref_tempdir} >/dev/null
|
||||
git checkout ${reference_ref} >/dev/null 2>/dev/null
|
||||
go test -bench=. -benchmem | tee ${ref_benchmark}
|
||||
popd >/dev/null
|
||||
|
||||
echo ""
|
||||
echo "=== local"
|
||||
go test -bench=. -benchmem | tee ${local_benchmark}
|
||||
|
||||
echo ""
|
||||
echo "=== diff"
|
||||
benchstat -delta-test=none ${ref_benchmark} ${local_benchmark}
|
244
vendor/github.com/pelletier/go-toml/benchmark.toml
generated
vendored
Normal file
244
vendor/github.com/pelletier/go-toml/benchmark.toml
generated
vendored
Normal file
|
@ -0,0 +1,244 @@
|
|||
################################################################################
|
||||
## Comment
|
||||
|
||||
# Speak your mind with the hash symbol. They go from the symbol to the end of
|
||||
# the line.
|
||||
|
||||
|
||||
################################################################################
|
||||
## Table
|
||||
|
||||
# Tables (also known as hash tables or dictionaries) are collections of
|
||||
# key/value pairs. They appear in square brackets on a line by themselves.
|
||||
|
||||
[table]
|
||||
|
||||
key = "value" # Yeah, you can do this.
|
||||
|
||||
# Nested tables are denoted by table names with dots in them. Name your tables
|
||||
# whatever crap you please, just don't use #, ., [ or ].
|
||||
|
||||
[table.subtable]
|
||||
|
||||
key = "another value"
|
||||
|
||||
# You don't need to specify all the super-tables if you don't want to. TOML
|
||||
# knows how to do it for you.
|
||||
|
||||
# [x] you
|
||||
# [x.y] don't
|
||||
# [x.y.z] need these
|
||||
[x.y.z.w] # for this to work
|
||||
|
||||
|
||||
################################################################################
|
||||
## Inline Table
|
||||
|
||||
# Inline tables provide a more compact syntax for expressing tables. They are
|
||||
# especially useful for grouped data that can otherwise quickly become verbose.
|
||||
# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
|
||||
# allowed between the curly braces unless they are valid within a value.
|
||||
|
||||
[table.inline]
|
||||
|
||||
name = { first = "Tom", last = "Preston-Werner" }
|
||||
point = { x = 1, y = 2 }
|
||||
|
||||
|
||||
################################################################################
|
||||
## String
|
||||
|
||||
# There are four ways to express strings: basic, multi-line basic, literal, and
|
||||
# multi-line literal. All strings must contain only valid UTF-8 characters.
|
||||
|
||||
[string.basic]
|
||||
|
||||
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
|
||||
|
||||
[string.multiline]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "One\nTwo"
|
||||
key2 = """One\nTwo"""
|
||||
key3 = """
|
||||
One
|
||||
Two"""
|
||||
|
||||
[string.multiline.continued]
|
||||
|
||||
# The following strings are byte-for-byte equivalent:
|
||||
key1 = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
key2 = """
|
||||
The quick brown \
|
||||
|
||||
|
||||
fox jumps over \
|
||||
the lazy dog."""
|
||||
|
||||
key3 = """\
|
||||
The quick brown \
|
||||
fox jumps over \
|
||||
the lazy dog.\
|
||||
"""
|
||||
|
||||
[string.literal]
|
||||
|
||||
# What you see is what you get.
|
||||
winpath = 'C:\Users\nodejs\templates'
|
||||
winpath2 = '\\ServerX\admin$\system32\'
|
||||
quoted = 'Tom "Dubs" Preston-Werner'
|
||||
regex = '<\i\c*\s*>'
|
||||
|
||||
|
||||
[string.literal.multiline]
|
||||
|
||||
regex2 = '''I [dw]on't need \d{2} apples'''
|
||||
lines = '''
|
||||
The first newline is
|
||||
trimmed in raw strings.
|
||||
All other whitespace
|
||||
is preserved.
|
||||
'''
|
||||
|
||||
|
||||
################################################################################
|
||||
## Integer
|
||||
|
||||
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
|
||||
# Negative numbers are prefixed with a minus sign.
|
||||
|
||||
[integer]
|
||||
|
||||
key1 = +99
|
||||
key2 = 42
|
||||
key3 = 0
|
||||
key4 = -17
|
||||
|
||||
[integer.underscores]
|
||||
|
||||
# For large numbers, you may use underscores to enhance readability. Each
|
||||
# underscore must be surrounded by at least one digit.
|
||||
key1 = 1_000
|
||||
key2 = 5_349_221
|
||||
key3 = 1_2_3_4_5 # valid but inadvisable
|
||||
|
||||
|
||||
################################################################################
|
||||
## Float
|
||||
|
||||
# A float consists of an integer part (which may be prefixed with a plus or
|
||||
# minus sign) followed by a fractional part and/or an exponent part.
|
||||
|
||||
[float.fractional]
|
||||
|
||||
key1 = +1.0
|
||||
key2 = 3.1415
|
||||
key3 = -0.01
|
||||
|
||||
[float.exponent]
|
||||
|
||||
key1 = 5e+22
|
||||
key2 = 1e6
|
||||
key3 = -2E-2
|
||||
|
||||
[float.both]
|
||||
|
||||
key = 6.626e-34
|
||||
|
||||
[float.underscores]
|
||||
|
||||
key1 = 9_224_617.445_991_228_313
|
||||
key2 = 1e1_00
|
||||
|
||||
|
||||
################################################################################
|
||||
## Boolean
|
||||
|
||||
# Booleans are just the tokens you're used to. Always lowercase.
|
||||
|
||||
[boolean]
|
||||
|
||||
True = true
|
||||
False = false
|
||||
|
||||
|
||||
################################################################################
|
||||
## Datetime
|
||||
|
||||
# Datetimes are RFC 3339 dates.
|
||||
|
||||
[datetime]
|
||||
|
||||
key1 = 1979-05-27T07:32:00Z
|
||||
key2 = 1979-05-27T00:32:00-07:00
|
||||
key3 = 1979-05-27T00:32:00.999999-07:00
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array
|
||||
|
||||
# Arrays are square brackets with other primitives inside. Whitespace is
|
||||
# ignored. Elements are separated by commas. Data types may not be mixed.
|
||||
|
||||
[array]
|
||||
|
||||
key1 = [ 1, 2, 3 ]
|
||||
key2 = [ "red", "yellow", "green" ]
|
||||
key3 = [ [ 1, 2 ], [3, 4, 5] ]
|
||||
#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
|
||||
|
||||
# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
|
||||
# also ignore newlines between the brackets. Terminating commas are ok before
|
||||
# the closing bracket.
|
||||
|
||||
key5 = [
|
||||
1, 2, 3
|
||||
]
|
||||
key6 = [
|
||||
1,
|
||||
2, # this is ok
|
||||
]
|
||||
|
||||
|
||||
################################################################################
|
||||
## Array of Tables
|
||||
|
||||
# These can be expressed by using a table name in double brackets. Each table
|
||||
# with the same double bracketed name will be an element in the array. The
|
||||
# tables are inserted in the order encountered.
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Hammer"
|
||||
sku = 738594937
|
||||
|
||||
[[products]]
|
||||
|
||||
[[products]]
|
||||
|
||||
name = "Nail"
|
||||
sku = 284758393
|
||||
color = "gray"
|
||||
|
||||
|
||||
# You can create nested arrays of tables as well.
|
||||
|
||||
[[fruit]]
|
||||
name = "apple"
|
||||
|
||||
[fruit.physical]
|
||||
color = "red"
|
||||
shape = "round"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "red delicious"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "granny smith"
|
||||
|
||||
[[fruit]]
|
||||
name = "banana"
|
||||
|
||||
[[fruit.variety]]
|
||||
name = "plantain"
|
121
vendor/github.com/pelletier/go-toml/benchmark.yml
generated
vendored
Normal file
121
vendor/github.com/pelletier/go-toml/benchmark.yml
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
---
|
||||
array:
|
||||
key1:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
key2:
|
||||
- red
|
||||
- yellow
|
||||
- green
|
||||
key3:
|
||||
- - 1
|
||||
- 2
|
||||
- - 3
|
||||
- 4
|
||||
- 5
|
||||
key4:
|
||||
- - 1
|
||||
- 2
|
||||
- - a
|
||||
- b
|
||||
- c
|
||||
key5:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
key6:
|
||||
- 1
|
||||
- 2
|
||||
boolean:
|
||||
'False': false
|
||||
'True': true
|
||||
datetime:
|
||||
key1: '1979-05-27T07:32:00Z'
|
||||
key2: '1979-05-27T00:32:00-07:00'
|
||||
key3: '1979-05-27T00:32:00.999999-07:00'
|
||||
float:
|
||||
both:
|
||||
key: 6.626e-34
|
||||
exponent:
|
||||
key1: 5.0e+22
|
||||
key2: 1000000
|
||||
key3: -0.02
|
||||
fractional:
|
||||
key1: 1
|
||||
key2: 3.1415
|
||||
key3: -0.01
|
||||
underscores:
|
||||
key1: 9224617.445991227
|
||||
key2: 1.0e+100
|
||||
fruit:
|
||||
- name: apple
|
||||
physical:
|
||||
color: red
|
||||
shape: round
|
||||
variety:
|
||||
- name: red delicious
|
||||
- name: granny smith
|
||||
- name: banana
|
||||
variety:
|
||||
- name: plantain
|
||||
integer:
|
||||
key1: 99
|
||||
key2: 42
|
||||
key3: 0
|
||||
key4: -17
|
||||
underscores:
|
||||
key1: 1000
|
||||
key2: 5349221
|
||||
key3: 12345
|
||||
products:
|
||||
- name: Hammer
|
||||
sku: 738594937
|
||||
- {}
|
||||
- color: gray
|
||||
name: Nail
|
||||
sku: 284758393
|
||||
string:
|
||||
basic:
|
||||
basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
|
||||
literal:
|
||||
multiline:
|
||||
lines: |
|
||||
The first newline is
|
||||
trimmed in raw strings.
|
||||
All other whitespace
|
||||
is preserved.
|
||||
regex2: I [dw]on't need \d{2} apples
|
||||
quoted: Tom "Dubs" Preston-Werner
|
||||
regex: "<\\i\\c*\\s*>"
|
||||
winpath: C:\Users\nodejs\templates
|
||||
winpath2: "\\\\ServerX\\admin$\\system32\\"
|
||||
multiline:
|
||||
continued:
|
||||
key1: The quick brown fox jumps over the lazy dog.
|
||||
key2: The quick brown fox jumps over the lazy dog.
|
||||
key3: The quick brown fox jumps over the lazy dog.
|
||||
key1: |-
|
||||
One
|
||||
Two
|
||||
key2: |-
|
||||
One
|
||||
Two
|
||||
key3: |-
|
||||
One
|
||||
Two
|
||||
table:
|
||||
inline:
|
||||
name:
|
||||
first: Tom
|
||||
last: Preston-Werner
|
||||
point:
|
||||
x: 1
|
||||
y: 2
|
||||
key: value
|
||||
subtable:
|
||||
key: another value
|
||||
x:
|
||||
y:
|
||||
z:
|
||||
w: {}
|
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.4.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
|
29
vendor/github.com/pelletier/go-toml/example-crlf.toml
generated
vendored
Normal file
29
vendor/github.com/pelletier/go-toml/example-crlf.toml
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# This is a TOML document. Boom.
|
||||
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
organization = "GitHub"
|
||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002 ]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
|
||||
[servers]
|
||||
|
||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
[clients]
|
||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
29
vendor/github.com/pelletier/go-toml/example.toml
generated
vendored
Normal file
29
vendor/github.com/pelletier/go-toml/example.toml
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# This is a TOML document. Boom.
|
||||
|
||||
title = "TOML Example"
|
||||
|
||||
[owner]
|
||||
name = "Tom Preston-Werner"
|
||||
organization = "GitHub"
|
||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||
|
||||
[database]
|
||||
server = "192.168.1.1"
|
||||
ports = [ 8001, 8001, 8002 ]
|
||||
connection_max = 5000
|
||||
enabled = true
|
||||
|
||||
[servers]
|
||||
|
||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||
[servers.alpha]
|
||||
ip = "10.0.0.1"
|
||||
dc = "eqdc10"
|
||||
|
||||
[servers.beta]
|
||||
ip = "10.0.0.2"
|
||||
dc = "eqdc10"
|
||||
|
||||
[clients]
|
||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
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
|
||||
}
|
15
vendor/github.com/pelletier/go-toml/fuzz.sh
generated
vendored
Normal file
15
vendor/github.com/pelletier/go-toml/fuzz.sh
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
#! /bin/sh
|
||||
set -eu
|
||||
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz
|
||||
go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
||||
if [ ! -e toml-fuzz.zip ]; then
|
||||
go-fuzz-build github.com/pelletier/go-toml
|
||||
fi
|
||||
|
||||
rm -fr fuzz
|
||||
mkdir -p fuzz/corpus
|
||||
cp *.toml fuzz/corpus
|
||||
|
||||
go-fuzz -bin=toml-fuzz.zip -workdir=fuzz
|
9
vendor/github.com/pelletier/go-toml/go.mod
generated
vendored
Normal file
9
vendor/github.com/pelletier/go-toml/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
module github.com/pelletier/go-toml
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
7
vendor/github.com/pelletier/go-toml/go.sum
generated
vendored
Normal file
7
vendor/github.com/pelletier/go-toml/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
113
vendor/github.com/pelletier/go-toml/keysparsing.go
generated
vendored
Normal file
113
vendor/github.com/pelletier/go-toml/keysparsing.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Parsing keys handling both bare and quoted keys.
|
||||
|
||||
package toml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// 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 == '-' || unicode.IsNumber(r)
|
||||
}
|
752
vendor/github.com/pelletier/go-toml/lexer.go
generated
vendored
Normal file
752
vendor/github.com/pelletier/go-toml/lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,752 @@
|
|||
// 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
|
||||
depth int
|
||||
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 '[':
|
||||
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 l.depth > 0 {
|
||||
return l.lexRvalue
|
||||
}
|
||||
|
||||
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 '[':
|
||||
l.depth++
|
||||
return l.lexLeftBracket
|
||||
case ']':
|
||||
l.depth--
|
||||
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 l.depth == 0 {
|
||||
return l.lexVoid
|
||||
}
|
||||
return l.lexRvalue
|
||||
case '_':
|
||||
return l.errorf("cannot start number with underscore")
|
||||
}
|
||||
|
||||
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)
|
||||
dateMatch := dateRegexp.FindString(possibleDate)
|
||||
if dateMatch != "" {
|
||||
l.fastForward(len(dateMatch))
|
||||
return l.lexDate
|
||||
}
|
||||
|
||||
if next == '+' || next == '-' || isDigit(next) {
|
||||
return l.lexNumber
|
||||
}
|
||||
|
||||
if isAlphanumeric(next) {
|
||||
return l.lexKey
|
||||
}
|
||||
|
||||
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)
|
||||
return l.lexRvalue
|
||||
}
|
||||
|
||||
func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn {
|
||||
l.next()
|
||||
l.emit(tokenRightCurlyBrace)
|
||||
return l.lexRvalue
|
||||
}
|
||||
|
||||
func (l *tomlLexer) lexDate() tomlLexStateFn {
|
||||
l.emit(tokenDate)
|
||||
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)
|
||||
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 {
|
||||
growingString := ""
|
||||
|
||||
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())
|
||||
}
|
||||
growingString += "\"" + str + "\""
|
||||
l.next()
|
||||
continue
|
||||
} else if r == '\'' {
|
||||
l.next()
|
||||
str, err := l.lexLiteralStringAsString(`'`, false)
|
||||
if err != nil {
|
||||
return l.errorf(err.Error())
|
||||
}
|
||||
growingString += "'" + str + "'"
|
||||
l.next()
|
||||
continue
|
||||
} else if r == '\n' {
|
||||
return l.errorf("keys cannot contain new lines")
|
||||
} else if isSpace(r) {
|
||||
break
|
||||
} else if r == '.' {
|
||||
// skip
|
||||
} else if !isValidBareChar(r) {
|
||||
return l.errorf("keys cannot contain %c character", r)
|
||||
}
|
||||
growingString += string(r)
|
||||
l.next()
|
||||
}
|
||||
l.emitWithValue(tokenKey, growingString)
|
||||
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)
|
||||
return l.lexRvalue
|
||||
}
|
||||
|
||||
func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) {
|
||||
growingString := ""
|
||||
|
||||
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 growingString, nil
|
||||
}
|
||||
|
||||
next := l.peek()
|
||||
if next == eof {
|
||||
break
|
||||
}
|
||||
growingString += string(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) {
|
||||
growingString := ""
|
||||
|
||||
if discardLeadingNewLine {
|
||||
if l.follow("\r\n") {
|
||||
l.skip()
|
||||
l.skip()
|
||||
} else if l.peek() == '\n' {
|
||||
l.skip()
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if l.follow(terminator) {
|
||||
return growingString, 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 '"':
|
||||
growingString += "\""
|
||||
l.next()
|
||||
case 'n':
|
||||
growingString += "\n"
|
||||
l.next()
|
||||
case 'b':
|
||||
growingString += "\b"
|
||||
l.next()
|
||||
case 'f':
|
||||
growingString += "\f"
|
||||
l.next()
|
||||
case '/':
|
||||
growingString += "/"
|
||||
l.next()
|
||||
case 't':
|
||||
growingString += "\t"
|
||||
l.next()
|
||||
case 'r':
|
||||
growingString += "\r"
|
||||
l.next()
|
||||
case '\\':
|
||||
growingString += "\\"
|
||||
l.next()
|
||||
case 'u':
|
||||
l.next()
|
||||
code := ""
|
||||
for i := 0; i < 4; i++ {
|
||||
c := l.peek()
|
||||
if !isHexDigit(c) {
|
||||
return "", errors.New("unfinished unicode escape")
|
||||
}
|
||||
l.next()
|
||||
code = code + string(c)
|
||||
}
|
||||
intcode, err := strconv.ParseInt(code, 16, 32)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid unicode escape: \\u" + code)
|
||||
}
|
||||
growingString += string(rune(intcode))
|
||||
case 'U':
|
||||
l.next()
|
||||
code := ""
|
||||
for i := 0; i < 8; i++ {
|
||||
c := l.peek()
|
||||
if !isHexDigit(c) {
|
||||
return "", errors.New("unfinished unicode escape")
|
||||
}
|
||||
l.next()
|
||||
code = code + string(c)
|
||||
}
|
||||
intcode, err := strconv.ParseInt(code, 16, 64)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid unicode escape: \\U" + code)
|
||||
}
|
||||
growingString += string(rune(intcode))
|
||||
default:
|
||||
return "", errors.New("invalid escape sequence: \\" + string(l.peek()))
|
||||
}
|
||||
} else {
|
||||
r := l.peek()
|
||||
|
||||
if 0x00 <= r && r <= 0x1F && !(acceptNewLines && (r == '\n' || r == '\r')) {
|
||||
return "", fmt.Errorf("unescaped control character %U", r)
|
||||
}
|
||||
l.next()
|
||||
growingString += string(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)
|
||||
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() {
|
||||
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
|
||||
}
|
803
vendor/github.com/pelletier/go-toml/marshal.go
generated
vendored
Normal file
803
vendor/github.com/pelletier/go-toml/marshal.go
generated
vendored
Normal file
|
@ -0,0 +1,803 @@
|
|||
package toml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
tagFieldName = "toml"
|
||||
tagFieldComment = "comment"
|
||||
tagCommented = "commented"
|
||||
tagMultiline = "multiline"
|
||||
tagDefault = "default"
|
||||
)
|
||||
|
||||
type tomlOpts struct {
|
||||
name string
|
||||
comment string
|
||||
commented bool
|
||||
multiline bool
|
||||
include bool
|
||||
omitempty bool
|
||||
defaultValue string
|
||||
}
|
||||
|
||||
type encOpts struct {
|
||||
quoteMapKeys bool
|
||||
arraysOneElementPerLine bool
|
||||
}
|
||||
|
||||
var encOptsDefaults = encOpts{
|
||||
quoteMapKeys: false,
|
||||
}
|
||||
|
||||
type annotation struct {
|
||||
tag string
|
||||
comment string
|
||||
commented string
|
||||
multiline string
|
||||
defaultValue string
|
||||
}
|
||||
|
||||
var annotationDefault = annotation{
|
||||
tag: tagFieldName,
|
||||
comment: tagFieldComment,
|
||||
commented: tagCommented,
|
||||
multiline: tagMultiline,
|
||||
defaultValue: tagDefault,
|
||||
}
|
||||
|
||||
type marshalOrder int
|
||||
|
||||
// Orders the Encoder can write the fields to the output stream.
|
||||
const (
|
||||
// Sort fields alphabetically.
|
||||
OrderAlphabetical marshalOrder = iota + 1
|
||||
// Preserve the order the fields are encountered. For example, the order of fields in
|
||||
// a struct.
|
||||
OrderPreserve
|
||||
)
|
||||
|
||||
var timeType = reflect.TypeOf(time.Time{})
|
||||
var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
||||
|
||||
// Check if the given marshal type maps to a Tree primitive
|
||||
func isPrimitive(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Ptr:
|
||||
return isPrimitive(mtype.Elem())
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
return mtype == timeType || isCustomMarshaler(mtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given marshal type maps to a Tree slice
|
||||
func isTreeSlice(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Slice:
|
||||
return !isOtherSlice(mtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given marshal type maps to a non-Tree slice
|
||||
func isOtherSlice(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Ptr:
|
||||
return isOtherSlice(mtype.Elem())
|
||||
case reflect.Slice:
|
||||
return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem())
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given marshal type maps to a Tree
|
||||
func isTree(mtype reflect.Type) bool {
|
||||
switch mtype.Kind() {
|
||||
case reflect.Map:
|
||||
return true
|
||||
case reflect.Struct:
|
||||
return !isPrimitive(mtype)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isCustomMarshaler(mtype reflect.Type) bool {
|
||||
return mtype.Implements(marshalerType)
|
||||
}
|
||||
|
||||
func callCustomMarshaler(mval reflect.Value) ([]byte, error) {
|
||||
return mval.Interface().(Marshaler).MarshalTOML()
|
||||
}
|
||||
|
||||
// Marshaler is the interface implemented by types that
|
||||
// can marshal themselves into valid TOML.
|
||||
type Marshaler interface {
|
||||
MarshalTOML() ([]byte, error)
|
||||
}
|
||||
|
||||
/*
|
||||
Marshal returns the TOML encoding of v. Behavior is similar to the Go json
|
||||
encoder, except that there is no concept of a Marshaler interface or MarshalTOML
|
||||
function for sub-structs, and currently only definite types can be marshaled
|
||||
(i.e. no `interface{}`).
|
||||
|
||||
The following struct annotations are supported:
|
||||
|
||||
toml:"Field" Overrides the field's name to output.
|
||||
omitempty When set, empty values and groups are not emitted.
|
||||
comment:"comment" Emits a # comment on the same line. This supports new lines.
|
||||
commented:"true" Emits the value as commented.
|
||||
|
||||
Note that pointers are automatically assigned the "omitempty" option, as TOML
|
||||
explicitly does not handle null values (saying instead the label should be
|
||||
dropped).
|
||||
|
||||
Tree structural types and corresponding marshal types:
|
||||
|
||||
*Tree (*)struct, (*)map[string]interface{}
|
||||
[]*Tree (*)[](*)struct, (*)[](*)map[string]interface{}
|
||||
[]interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{})
|
||||
interface{} (*)primitive
|
||||
|
||||
Tree primitive types and corresponding marshal types:
|
||||
|
||||
uint64 uint, uint8-uint64, pointers to same
|
||||
int64 int, int8-uint64, pointers to same
|
||||
float64 float32, float64, pointers to same
|
||||
string string, pointers to same
|
||||
bool bool, pointers to same
|
||||
time.Time time.Time{}, pointers to same
|
||||
|
||||
For additional flexibility, use the Encoder API.
|
||||
*/
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return NewEncoder(nil).marshal(v)
|
||||
}
|
||||
|
||||
// Encoder writes TOML values to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
encOpts
|
||||
annotation
|
||||
line int
|
||||
col int
|
||||
order marshalOrder
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{
|
||||
w: w,
|
||||
encOpts: encOptsDefaults,
|
||||
annotation: annotationDefault,
|
||||
line: 0,
|
||||
col: 1,
|
||||
order: OrderAlphabetical,
|
||||
}
|
||||
}
|
||||
|
||||
// Encode writes the TOML encoding of v to the stream.
|
||||
//
|
||||
// See the documentation for Marshal for details.
|
||||
func (e *Encoder) Encode(v interface{}) error {
|
||||
b, err := e.marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := e.w.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QuoteMapKeys sets up the encoder to encode
|
||||
// maps with string type keys with quoted TOML keys.
|
||||
//
|
||||
// This relieves the character limitations on map keys.
|
||||
func (e *Encoder) QuoteMapKeys(v bool) *Encoder {
|
||||
e.quoteMapKeys = v
|
||||
return e
|
||||
}
|
||||
|
||||
// ArraysWithOneElementPerLine sets up the encoder to encode arrays
|
||||
// with more than one element on multiple lines instead of one.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// A = [1,2,3]
|
||||
//
|
||||
// Becomes
|
||||
//
|
||||
// A = [
|
||||
// 1,
|
||||
// 2,
|
||||
// 3,
|
||||
// ]
|
||||
func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder {
|
||||
e.arraysOneElementPerLine = v
|
||||
return e
|
||||
}
|
||||
|
||||
// Order allows to change in which order fields will be written to the output stream.
|
||||
func (e *Encoder) Order(ord marshalOrder) *Encoder {
|
||||
e.order = ord
|
||||
return e
|
||||
}
|
||||
|
||||
// SetTagName allows changing default tag "toml"
|
||||
func (e *Encoder) SetTagName(v string) *Encoder {
|
||||
e.tag = v
|
||||
return e
|
||||
}
|
||||
|
||||
// SetTagComment allows changing default tag "comment"
|
||||
func (e *Encoder) SetTagComment(v string) *Encoder {
|
||||
e.comment = v
|
||||
return e
|
||||
}
|
||||
|
||||
// SetTagCommented allows changing default tag "commented"
|
||||
func (e *Encoder) SetTagCommented(v string) *Encoder {
|
||||
e.commented = v
|
||||
return e
|
||||
}
|
||||
|
||||
// SetTagMultiline allows changing default tag "multiline"
|
||||
func (e *Encoder) SetTagMultiline(v string) *Encoder {
|
||||
e.multiline = v
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Encoder) marshal(v interface{}) ([]byte, error) {
|
||||
mtype := reflect.TypeOf(v)
|
||||
|
||||
switch mtype.Kind() {
|
||||
case reflect.Struct, reflect.Map:
|
||||
case reflect.Ptr:
|
||||
if mtype.Elem().Kind() != reflect.Struct {
|
||||
return []byte{}, errors.New("Only pointer to struct can be marshaled to TOML")
|
||||
}
|
||||
default:
|
||||
return []byte{}, errors.New("Only a struct or map can be marshaled to TOML")
|
||||
}
|
||||
|
||||
sval := reflect.ValueOf(v)
|
||||
if isCustomMarshaler(mtype) {
|
||||
return callCustomMarshaler(sval)
|
||||
}
|
||||
t, err := e.valueToTree(mtype, sval)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = t.writeToOrdered(&buf, "", "", 0, e.arraysOneElementPerLine, e.order)
|
||||
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// Create next tree with a position based on Encoder.line
|
||||
func (e *Encoder) nextTree() *Tree {
|
||||
return newTreeWithPosition(Position{Line: e.line, Col: 1})
|
||||
}
|
||||
|
||||
// Convert given marshal struct or map value to toml tree
|
||||
func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return e.valueToTree(mtype.Elem(), mval.Elem())
|
||||
}
|
||||
tval := e.nextTree()
|
||||
switch mtype.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < mtype.NumField(); i++ {
|
||||
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
||||
opts := tomlOptions(mtypef, e.annotation)
|
||||
if opts.include && (!opts.omitempty || !isZero(mvalf)) {
|
||||
val, err := e.valueToToml(mtypef.Type, mvalf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tval.SetWithOptions(opts.name, SetOptions{
|
||||
Comment: opts.comment,
|
||||
Commented: opts.commented,
|
||||
Multiline: opts.multiline,
|
||||
}, val)
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
keys := mval.MapKeys()
|
||||
if e.order == OrderPreserve && len(keys) > 0 {
|
||||
// Sorting []reflect.Value is not straight forward.
|
||||
//
|
||||
// OrderPreserve will support deterministic results when string is used
|
||||
// as the key to maps.
|
||||
typ := keys[0].Type()
|
||||
kind := keys[0].Kind()
|
||||
if kind == reflect.String {
|
||||
ikeys := make([]string, len(keys))
|
||||
for i := range keys {
|
||||
ikeys[i] = keys[i].Interface().(string)
|
||||
}
|
||||
sort.Strings(ikeys)
|
||||
for i := range ikeys {
|
||||
keys[i] = reflect.ValueOf(ikeys[i]).Convert(typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, key := range keys {
|
||||
mvalf := mval.MapIndex(key)
|
||||
val, err := e.valueToToml(mtype.Elem(), mvalf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if e.quoteMapKeys {
|
||||
keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval.SetPath([]string{keyStr}, val)
|
||||
} else {
|
||||
tval.Set(key.String(), val)
|
||||
}
|
||||
}
|
||||
}
|
||||
return tval, nil
|
||||
}
|
||||
|
||||
// Convert given marshal slice to slice of Toml trees
|
||||
func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) {
|
||||
tval := make([]*Tree, mval.Len(), mval.Len())
|
||||
for i := 0; i < mval.Len(); i++ {
|
||||
val, err := e.valueToTree(mtype.Elem(), mval.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval[i] = val
|
||||
}
|
||||
return tval, nil
|
||||
}
|
||||
|
||||
// Convert given marshal slice to slice of toml values
|
||||
func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||
tval := make([]interface{}, mval.Len(), mval.Len())
|
||||
for i := 0; i < mval.Len(); i++ {
|
||||
val, err := e.valueToToml(mtype.Elem(), mval.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tval[i] = val
|
||||
}
|
||||
return tval, nil
|
||||
}
|
||||
|
||||
// Convert given marshal value to toml value
|
||||
func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||
e.line++
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return e.valueToToml(mtype.Elem(), mval.Elem())
|
||||
}
|
||||
switch {
|
||||
case isCustomMarshaler(mtype):
|
||||
return callCustomMarshaler(mval)
|
||||
case isTree(mtype):
|
||||
return e.valueToTree(mtype, mval)
|
||||
case isTreeSlice(mtype):
|
||||
return e.valueToTreeSlice(mtype, mval)
|
||||
case isOtherSlice(mtype):
|
||||
return e.valueToOtherSlice(mtype, mval)
|
||||
default:
|
||||
switch mtype.Kind() {
|
||||
case reflect.Bool:
|
||||
return mval.Bool(), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) {
|
||||
return fmt.Sprint(mval), nil
|
||||
}
|
||||
return mval.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return mval.Uint(), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return mval.Float(), nil
|
||||
case reflect.String:
|
||||
return mval.String(), nil
|
||||
case reflect.Struct:
|
||||
return mval.Interface().(time.Time), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v.
|
||||
// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for
|
||||
// sub-structs, and only definite types can be unmarshaled.
|
||||
func (t *Tree) Unmarshal(v interface{}) error {
|
||||
d := Decoder{tval: t, tagName: tagFieldName}
|
||||
return d.unmarshal(v)
|
||||
}
|
||||
|
||||
// Marshal returns the TOML encoding of Tree.
|
||||
// See Marshal() documentation for types mapping table.
|
||||
func (t *Tree) Marshal() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err := NewEncoder(&buf).Encode(t)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// Unmarshal parses the TOML-encoded data and stores the result in the value
|
||||
// pointed to by v. Behavior is similar to the Go json encoder, except that there
|
||||
// is no concept of an Unmarshaler interface or UnmarshalTOML function for
|
||||
// sub-structs, and currently only definite types can be unmarshaled to (i.e. no
|
||||
// `interface{}`).
|
||||
//
|
||||
// The following struct annotations are supported:
|
||||
//
|
||||
// toml:"Field" Overrides the field's name to map to.
|
||||
// default:"foo" Provides a default value.
|
||||
//
|
||||
// For default values, only fields of the following types are supported:
|
||||
// * string
|
||||
// * bool
|
||||
// * int
|
||||
// * int64
|
||||
// * float64
|
||||
//
|
||||
// See Marshal() documentation for types mapping table.
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
t, err := LoadReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.Unmarshal(v)
|
||||
}
|
||||
|
||||
// Decoder reads and decodes TOML values from an input stream.
|
||||
type Decoder struct {
|
||||
r io.Reader
|
||||
tval *Tree
|
||||
encOpts
|
||||
tagName string
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{
|
||||
r: r,
|
||||
encOpts: encOptsDefaults,
|
||||
tagName: tagFieldName,
|
||||
}
|
||||
}
|
||||
|
||||
// Decode reads a TOML-encoded value from it's input
|
||||
// and unmarshals it in the value pointed at by v.
|
||||
//
|
||||
// See the documentation for Marshal for details.
|
||||
func (d *Decoder) Decode(v interface{}) error {
|
||||
var err error
|
||||
d.tval, err = LoadReader(d.r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.unmarshal(v)
|
||||
}
|
||||
|
||||
// SetTagName allows changing default tag "toml"
|
||||
func (d *Decoder) SetTagName(v string) *Decoder {
|
||||
d.tagName = v
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decoder) unmarshal(v interface{}) error {
|
||||
mtype := reflect.TypeOf(v)
|
||||
if mtype.Kind() != reflect.Ptr {
|
||||
return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
|
||||
}
|
||||
|
||||
elem := mtype.Elem()
|
||||
|
||||
switch elem.Kind() {
|
||||
case reflect.Struct, reflect.Map:
|
||||
default:
|
||||
return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
|
||||
}
|
||||
|
||||
sval, err := d.valueFromTree(elem, d.tval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reflect.ValueOf(v).Elem().Set(sval)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert toml tree to marshal struct or map, using marshal type
|
||||
func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return d.unwrapPointer(mtype, tval)
|
||||
}
|
||||
var mval reflect.Value
|
||||
switch mtype.Kind() {
|
||||
case reflect.Struct:
|
||||
mval = reflect.New(mtype).Elem()
|
||||
for i := 0; i < mtype.NumField(); i++ {
|
||||
mtypef := mtype.Field(i)
|
||||
an := annotation{tag: d.tagName}
|
||||
opts := tomlOptions(mtypef, an)
|
||||
if opts.include {
|
||||
baseKey := opts.name
|
||||
keysToTry := []string{
|
||||
baseKey,
|
||||
strings.ToLower(baseKey),
|
||||
strings.ToTitle(baseKey),
|
||||
strings.ToLower(string(baseKey[0])) + baseKey[1:],
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, key := range keysToTry {
|
||||
exists := tval.Has(key)
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
val := tval.Get(key)
|
||||
mvalf, err := d.valueFromToml(mtypef.Type, val)
|
||||
if err != nil {
|
||||
return mval, formatError(err, tval.GetPosition(key))
|
||||
}
|
||||
mval.Field(i).Set(mvalf)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
if !found && opts.defaultValue != "" {
|
||||
mvalf := mval.Field(i)
|
||||
var val interface{}
|
||||
var err error
|
||||
switch mvalf.Kind() {
|
||||
case reflect.Bool:
|
||||
val, err = strconv.ParseBool(opts.defaultValue)
|
||||
if err != nil {
|
||||
return mval.Field(i), err
|
||||
}
|
||||
case reflect.Int:
|
||||
val, err = strconv.Atoi(opts.defaultValue)
|
||||
if err != nil {
|
||||
return mval.Field(i), err
|
||||
}
|
||||
case reflect.String:
|
||||
val = opts.defaultValue
|
||||
case reflect.Int64:
|
||||
val, err = strconv.ParseInt(opts.defaultValue, 10, 64)
|
||||
if err != nil {
|
||||
return mval.Field(i), err
|
||||
}
|
||||
case reflect.Float64:
|
||||
val, err = strconv.ParseFloat(opts.defaultValue, 64)
|
||||
if err != nil {
|
||||
return mval.Field(i), err
|
||||
}
|
||||
default:
|
||||
return mval.Field(i), fmt.Errorf("unsuported field type for default option")
|
||||
}
|
||||
mval.Field(i).Set(reflect.ValueOf(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
mval = reflect.MakeMap(mtype)
|
||||
for _, key := range tval.Keys() {
|
||||
// TODO: path splits key
|
||||
val := tval.GetPath([]string{key})
|
||||
mvalf, err := d.valueFromToml(mtype.Elem(), val)
|
||||
if err != nil {
|
||||
return mval, formatError(err, tval.GetPosition(key))
|
||||
}
|
||||
mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf)
|
||||
}
|
||||
}
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
// Convert toml value to marshal struct/map slice, using marshal type
|
||||
func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
|
||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||
for i := 0; i < len(tval); i++ {
|
||||
val, err := d.valueFromTree(mtype.Elem(), tval[i])
|
||||
if err != nil {
|
||||
return mval, err
|
||||
}
|
||||
mval.Index(i).Set(val)
|
||||
}
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
// Convert toml value to marshal primitive slice, using marshal type
|
||||
func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||
for i := 0; i < len(tval); i++ {
|
||||
val, err := d.valueFromToml(mtype.Elem(), tval[i])
|
||||
if err != nil {
|
||||
return mval, err
|
||||
}
|
||||
mval.Index(i).Set(val)
|
||||
}
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
// Convert toml value to marshal value, using marshal type
|
||||
func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
return d.unwrapPointer(mtype, tval)
|
||||
}
|
||||
|
||||
switch t := tval.(type) {
|
||||
case *Tree:
|
||||
if isTree(mtype) {
|
||||
return d.valueFromTree(mtype, t)
|
||||
}
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
|
||||
case []*Tree:
|
||||
if isTreeSlice(mtype) {
|
||||
return d.valueFromTreeSlice(mtype, t)
|
||||
}
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
|
||||
case []interface{}:
|
||||
if isOtherSlice(mtype) {
|
||||
return d.valueFromOtherSlice(mtype, t)
|
||||
}
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a slice", tval, tval)
|
||||
default:
|
||||
switch mtype.Kind() {
|
||||
case reflect.Bool, reflect.Struct:
|
||||
val := reflect.ValueOf(tval)
|
||||
// if this passes for when mtype is reflect.Struct, tval is a time.Time
|
||||
if !val.Type().ConvertibleTo(mtype) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
|
||||
}
|
||||
|
||||
return val.Convert(mtype), nil
|
||||
case reflect.String:
|
||||
val := reflect.ValueOf(tval)
|
||||
// stupidly, int64 is convertible to string. So special case this.
|
||||
if !val.Type().ConvertibleTo(mtype) || val.Kind() == reflect.Int64 {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
|
||||
}
|
||||
|
||||
return val.Convert(mtype), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
val := reflect.ValueOf(tval)
|
||||
if mtype.Kind() == reflect.Int64 && mtype == reflect.TypeOf(time.Duration(1)) && val.Kind() == reflect.String {
|
||||
d, err := time.ParseDuration(val.String())
|
||||
if err != nil {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v. %s", tval, tval, mtype.String(), err)
|
||||
}
|
||||
return reflect.ValueOf(d), nil
|
||||
}
|
||||
if !val.Type().ConvertibleTo(mtype) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
|
||||
}
|
||||
if reflect.Indirect(reflect.New(mtype)).OverflowInt(val.Convert(mtype).Int()) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String())
|
||||
}
|
||||
|
||||
return val.Convert(mtype), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
val := reflect.ValueOf(tval)
|
||||
if !val.Type().ConvertibleTo(mtype) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
|
||||
}
|
||||
|
||||
if val.Convert(reflect.TypeOf(int(1))).Int() < 0 {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("%v(%T) is negative so does not fit in %v", tval, tval, mtype.String())
|
||||
}
|
||||
if reflect.Indirect(reflect.New(mtype)).OverflowUint(uint64(val.Convert(mtype).Uint())) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String())
|
||||
}
|
||||
|
||||
return val.Convert(mtype), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
val := reflect.ValueOf(tval)
|
||||
if !val.Type().ConvertibleTo(mtype) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
|
||||
}
|
||||
if reflect.Indirect(reflect.New(mtype)).OverflowFloat(val.Convert(mtype).Float()) {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("%v(%T) would overflow %v", tval, tval, mtype.String())
|
||||
}
|
||||
|
||||
return val.Convert(mtype), nil
|
||||
default:
|
||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v(%v)", tval, tval, mtype, mtype.Kind())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||
val, err := d.valueFromToml(mtype.Elem(), tval)
|
||||
if err != nil {
|
||||
return reflect.ValueOf(nil), err
|
||||
}
|
||||
mval := reflect.New(mtype.Elem())
|
||||
mval.Elem().Set(val)
|
||||
return mval, nil
|
||||
}
|
||||
|
||||
func tomlOptions(vf reflect.StructField, an annotation) tomlOpts {
|
||||
tag := vf.Tag.Get(an.tag)
|
||||
parse := strings.Split(tag, ",")
|
||||
var comment string
|
||||
if c := vf.Tag.Get(an.comment); c != "" {
|
||||
comment = c
|
||||
}
|
||||
commented, _ := strconv.ParseBool(vf.Tag.Get(an.commented))
|
||||
multiline, _ := strconv.ParseBool(vf.Tag.Get(an.multiline))
|
||||
defaultValue := vf.Tag.Get(tagDefault)
|
||||
result := tomlOpts{
|
||||
name: vf.Name,
|
||||
comment: comment,
|
||||
commented: commented,
|
||||
multiline: multiline,
|
||||
include: true,
|
||||
omitempty: false,
|
||||
defaultValue: defaultValue,
|
||||
}
|
||||
if parse[0] != "" {
|
||||
if parse[0] == "-" && len(parse) == 1 {
|
||||
result.include = false
|
||||
} else {
|
||||
result.name = strings.Trim(parse[0], " ")
|
||||
}
|
||||
}
|
||||
if vf.PkgPath != "" {
|
||||
result.include = false
|
||||
}
|
||||
if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" {
|
||||
result.omitempty = true
|
||||
}
|
||||
if vf.Type.Kind() == reflect.Ptr {
|
||||
result.omitempty = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func isZero(val reflect.Value) bool {
|
||||
switch val.Type().Kind() {
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
return val.Len() == 0
|
||||
default:
|
||||
return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func formatError(err error, pos Position) error {
|
||||
if err.Error()[0] == '(' { // Error already contains position information
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s: %s", pos, err)
|
||||
}
|
17
vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_Map_test.toml
generated
vendored
Normal file
17
vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_Map_test.toml
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
title = "TOML Marshal Testing"
|
||||
|
||||
[basic_map]
|
||||
one = "one"
|
||||
two = "two"
|
||||
|
||||
[long_map]
|
||||
a7 = "1"
|
||||
b3 = "2"
|
||||
c8 = "3"
|
||||
d4 = "4"
|
||||
e6 = "5"
|
||||
f5 = "6"
|
||||
g10 = "7"
|
||||
h1 = "8"
|
||||
i2 = "9"
|
||||
j9 = "10"
|
38
vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
generated
vendored
Normal file
38
vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
title = "TOML Marshal Testing"
|
||||
|
||||
[basic_lists]
|
||||
floats = [12.3,45.6,78.9]
|
||||
bools = [true,false,true]
|
||||
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
||||
ints = [8001,8001,8002]
|
||||
uints = [5002,5003]
|
||||
strings = ["One","Two","Three"]
|
||||
|
||||
[[subdocptrs]]
|
||||
name = "Second"
|
||||
|
||||
[basic_map]
|
||||
one = "one"
|
||||
two = "two"
|
||||
|
||||
[subdoc]
|
||||
|
||||
[subdoc.second]
|
||||
name = "Second"
|
||||
|
||||
[subdoc.first]
|
||||
name = "First"
|
||||
|
||||
[basic]
|
||||
uint = 5001
|
||||
bool = true
|
||||
float = 123.4
|
||||
int = 5000
|
||||
string = "Bite me"
|
||||
date = 1979-05-27T07:32:00Z
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.First"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.Second"
|
38
vendor/github.com/pelletier/go-toml/marshal_test.toml
generated
vendored
Normal file
38
vendor/github.com/pelletier/go-toml/marshal_test.toml
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
title = "TOML Marshal Testing"
|
||||
|
||||
[basic]
|
||||
bool = true
|
||||
date = 1979-05-27T07:32:00Z
|
||||
float = 123.4
|
||||
int = 5000
|
||||
string = "Bite me"
|
||||
uint = 5001
|
||||
|
||||
[basic_lists]
|
||||
bools = [true,false,true]
|
||||
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
||||
floats = [12.3,45.6,78.9]
|
||||
ints = [8001,8001,8002]
|
||||
strings = ["One","Two","Three"]
|
||||
uints = [5002,5003]
|
||||
|
||||
[basic_map]
|
||||
one = "one"
|
||||
two = "two"
|
||||
|
||||
[subdoc]
|
||||
|
||||
[subdoc.first]
|
||||
name = "First"
|
||||
|
||||
[subdoc.second]
|
||||
name = "Second"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.First"
|
||||
|
||||
[[subdoclist]]
|
||||
name = "List.Second"
|
||||
|
||||
[[subdocptrs]]
|
||||
name = "Second"
|
442
vendor/github.com/pelletier/go-toml/parser.go
generated
vendored
Normal file
442
vendor/github.com/pelletier/go-toml/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,442 @@
|
|||
// 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)
|
||||
}
|
||||
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, "."))
|
||||
}
|
||||
|
||||
// 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:
|
||||
val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC)
|
||||
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)
|
||||
value := p.parseRvalue()
|
||||
tree.Set(key.val, value)
|
||||
case tokenComma:
|
||||
if previous == nil {
|
||||
p.raiseError(follow, "inline table cannot start with a comma")
|
||||
}
|
||||
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")
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
func (p *tomlParser) parseArray() interface{} {
|
||||
var array []interface{}
|
||||
arrayType := reflect.TypeOf(nil)
|
||||
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 arrayType == nil {
|
||||
arrayType = reflect.TypeOf(val)
|
||||
}
|
||||
if reflect.TypeOf(val) != arrayType {
|
||||
p.raiseError(follow, "mixed types in array")
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
144
vendor/github.com/pelletier/go-toml/token.go
generated
vendored
Normal file
144
vendor/github.com/pelletier/go-toml/token.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
|||
package toml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// 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
|
||||
tokenKeyGroup
|
||||
tokenKeyGroupArray
|
||||
tokenComma
|
||||
tokenColon
|
||||
tokenDollar
|
||||
tokenStar
|
||||
tokenQuestion
|
||||
tokenDot
|
||||
tokenDotDot
|
||||
tokenEOL
|
||||
)
|
||||
|
||||
var tokenTypeNames = []string{
|
||||
"Error",
|
||||
"EOF",
|
||||
"Comment",
|
||||
"Key",
|
||||
"String",
|
||||
"Integer",
|
||||
"True",
|
||||
"False",
|
||||
"Float",
|
||||
"Inf",
|
||||
"NaN",
|
||||
"=",
|
||||
"[",
|
||||
"]",
|
||||
"{",
|
||||
"}",
|
||||
"(",
|
||||
")",
|
||||
"]]",
|
||||
"[[",
|
||||
"Date",
|
||||
"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) Int() int {
|
||||
if result, err := strconv.Atoi(t.val); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
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 unicode.IsLetter(r) || 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 unicode.IsNumber(r)
|
||||
}
|
||||
|
||||
func isHexDigit(r rune) bool {
|
||||
return isDigit(r) ||
|
||||
(r >= 'a' && r <= 'f') ||
|
||||
(r >= 'A' && r <= 'F')
|
||||
}
|
393
vendor/github.com/pelletier/go-toml/toml.go
generated
vendored
Normal file
393
vendor/github.com/pelletier/go-toml/toml.go
generated
vendored
Normal file
|
@ -0,0 +1,393 @@
|
|||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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, "."))
|
||||
}
|
||||
|
||||
// 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
|
||||
subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
|
||||
}
|
||||
subtree = node[len(node)-1]
|
||||
}
|
||||
}
|
||||
|
||||
var toInsert interface{}
|
||||
|
||||
switch v := value.(type) {
|
||||
case *Tree:
|
||||
v.comment = opts.Comment
|
||||
toInsert = value
|
||||
case []*Tree:
|
||||
toInsert = value
|
||||
case *tomlValue:
|
||||
v.comment = opts.Comment
|
||||
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
|
||||
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)
|
||||
}
|
142
vendor/github.com/pelletier/go-toml/tomltree_create.go
generated
vendored
Normal file
142
vendor/github.com/pelletier/go-toml/tomltree_create.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
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
|
||||
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
|
||||
}
|
434
vendor/github.com/pelletier/go-toml/tomltree_write.go
generated
vendored
Normal file
434
vendor/github.com/pelletier/go-toml/tomltree_write.go
generated
vendored
Normal file
|
@ -0,0 +1,434 @@
|
|||
package toml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"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) 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()
|
||||
}
|
||||
|
||||
// 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 tomlValueStringRepresentation(v interface{}, indent string, 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:
|
||||
// Ensure a round float does contain a decimal point. Otherwise feeding
|
||||
// the output back to the parser would convert to an integer.
|
||||
if math.Trunc(value) == value {
|
||||
return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil
|
||||
}
|
||||
return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil
|
||||
case string:
|
||||
if tv.multiline {
|
||||
return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
|
||||
}
|
||||
return "\"" + encodeTomlString(value) + "\"", nil
|
||||
case []byte:
|
||||
b, _ := v.([]byte)
|
||||
return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine)
|
||||
case bool:
|
||||
if value {
|
||||
return "true", nil
|
||||
}
|
||||
return "false", nil
|
||||
case time.Time:
|
||||
return value.Format(time.RFC3339), nil
|
||||
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, indent, 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(value)
|
||||
stringBuffer.WriteString(`,`)
|
||||
stringBuffer.WriteString("\n")
|
||||
}
|
||||
|
||||
stringBuffer.WriteString(indent + "]")
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder) (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 := k
|
||||
if keyspace != "" {
|
||||
combinedKey = keyspace + "." + combinedKey
|
||||
}
|
||||
var commented string
|
||||
if t.commented {
|
||||
commented = "# "
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
|
||||
bytesCount += int64(writtenBytesCount)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
bytesCount, err = node.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
case []*Tree:
|
||||
for _, subTree := range node {
|
||||
writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
|
||||
bytesCount += int64(writtenBytesCount)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
|
||||
bytesCount, err = subTree.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord)
|
||||
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])
|
||||
}
|
||||
|
||||
repr, err := tomlValueStringRepresentation(v, indent, 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
|
||||
}
|
||||
}
|
||||
|
||||
var commented string
|
||||
if v.commented {
|
||||
commented = "# "
|
||||
}
|
||||
writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n")
|
||||
bytesCount += int64(writtenBytesCount)
|
||||
if err != nil {
|
||||
return bytesCount, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytesCount, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
var buf bytes.Buffer
|
||||
_, err := t.WriteTo(&buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), 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