summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/pelletier
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pelletier')
-rw-r--r--vendor/github.com/pelletier/go-toml/.dockerignore2
-rw-r--r--vendor/github.com/pelletier/go-toml/.gitignore3
-rw-r--r--vendor/github.com/pelletier/go-toml/.travis.yml23
-rw-r--r--vendor/github.com/pelletier/go-toml/CONTRIBUTING.md132
-rw-r--r--vendor/github.com/pelletier/go-toml/Dockerfile11
-rw-r--r--vendor/github.com/pelletier/go-toml/Makefile29
-rw-r--r--vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md5
-rw-r--r--vendor/github.com/pelletier/go-toml/README.md38
-rw-r--r--vendor/github.com/pelletier/go-toml/azure-pipelines.yml230
-rw-r--r--vendor/github.com/pelletier/go-toml/benchmark.sh5
-rw-r--r--vendor/github.com/pelletier/go-toml/doc.go2
-rw-r--r--vendor/github.com/pelletier/go-toml/fuzzit.sh26
-rw-r--r--vendor/github.com/pelletier/go-toml/go.mod9
-rw-r--r--vendor/github.com/pelletier/go-toml/go.sum17
-rw-r--r--vendor/github.com/pelletier/go-toml/keysparsing.go136
-rw-r--r--vendor/github.com/pelletier/go-toml/lexer.go44
-rw-r--r--vendor/github.com/pelletier/go-toml/localtime.go281
-rw-r--r--vendor/github.com/pelletier/go-toml/marshal.go505
-rw-r--r--vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml39
-rw-r--r--vendor/github.com/pelletier/go-toml/marshal_test.toml1
-rw-r--r--vendor/github.com/pelletier/go-toml/parser.go73
-rw-r--r--vendor/github.com/pelletier/go-toml/test.sh88
-rw-r--r--vendor/github.com/pelletier/go-toml/token.go13
-rw-r--r--vendor/github.com/pelletier/go-toml/toml.go124
-rw-r--r--vendor/github.com/pelletier/go-toml/tomltree_write.go322
25 files changed, 1723 insertions, 435 deletions
diff --git a/vendor/github.com/pelletier/go-toml/.dockerignore b/vendor/github.com/pelletier/go-toml/.dockerignore
new file mode 100644
index 00000000..7b588347
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/.dockerignore
@@ -0,0 +1,2 @@
+cmd/tomll/tomll
+cmd/tomljson/tomljson
diff --git a/vendor/github.com/pelletier/go-toml/.gitignore b/vendor/github.com/pelletier/go-toml/.gitignore
index 99e38bbc..e6ba63a5 100644
--- a/vendor/github.com/pelletier/go-toml/.gitignore
+++ b/vendor/github.com/pelletier/go-toml/.gitignore
@@ -1,2 +1,5 @@
test_program/test_program_bin
fuzz/
+cmd/tomll/tomll
+cmd/tomljson/tomljson
+cmd/tomltestgen/tomltestgen
diff --git a/vendor/github.com/pelletier/go-toml/.travis.yml b/vendor/github.com/pelletier/go-toml/.travis.yml
deleted file mode 100644
index c9fbf304..00000000
--- a/vendor/github.com/pelletier/go-toml/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-sudo: false
-language: go
-go:
- - 1.8.x
- - 1.9.x
- - 1.10.x
- - tip
-matrix:
- allow_failures:
- - go: tip
- fast_finish: true
-script:
- - if [ -n "$(go fmt ./...)" ]; then exit 1; fi
- - ./test.sh
- - ./benchmark.sh $TRAVIS_BRANCH https://github.com/$TRAVIS_REPO_SLUG.git
-before_install:
- - go get github.com/axw/gocov/gocov
- - go get github.com/mattn/goveralls
- - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
-branches:
- only: [master]
-after_success:
- - $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=coverage.out -repotoken $COVERALLS_TOKEN
diff --git a/vendor/github.com/pelletier/go-toml/CONTRIBUTING.md b/vendor/github.com/pelletier/go-toml/CONTRIBUTING.md
new file mode 100644
index 00000000..405c911c
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/CONTRIBUTING.md
@@ -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
diff --git a/vendor/github.com/pelletier/go-toml/Dockerfile b/vendor/github.com/pelletier/go-toml/Dockerfile
new file mode 100644
index 00000000..fffdb016
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/Dockerfile
@@ -0,0 +1,11 @@
+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
+COPY --from=builder /go/bin/jsontoml /usr/bin/jsontoml
diff --git a/vendor/github.com/pelletier/go-toml/Makefile b/vendor/github.com/pelletier/go-toml/Makefile
new file mode 100644
index 00000000..9e4503ae
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/Makefile
@@ -0,0 +1,29 @@
+export CGO_ENABLED=0
+go := go
+go.goos ?= $(shell echo `go version`|cut -f4 -d ' '|cut -d '/' -f1)
+go.goarch ?= $(shell echo `go version`|cut -f4 -d ' '|cut -d '/' -f2)
+
+out.tools := tomll tomljson jsontoml
+out.dist := $(out.tools:=_$(go.goos)_$(go.goarch).tar.xz)
+sources := $(wildcard **/*.go)
+
+
+.PHONY:
+tools: $(out.tools)
+
+$(out.tools): $(sources)
+ GOOS=$(go.goos) GOARCH=$(go.goarch) $(go) build ./cmd/$@
+
+.PHONY:
+dist: $(out.dist)
+
+$(out.dist):%_$(go.goos)_$(go.goarch).tar.xz: %
+ if [ "$(go.goos)" = "windows" ]; then \
+ tar -cJf $@ $^.exe; \
+ else \
+ tar -cJf $@ $^; \
+ fi
+
+.PHONY:
+clean:
+ rm -rf $(out.tools) $(out.dist)
diff --git a/vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md b/vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..041cdc4a
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/PULL_REQUEST_TEMPLATE.md
@@ -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).
diff --git a/vendor/github.com/pelletier/go-toml/README.md b/vendor/github.com/pelletier/go-toml/README.md
index 0d357acf..4ef303af 100644
--- a/vendor/github.com/pelletier/go-toml/README.md
+++ b/vendor/github.com/pelletier/go-toml/README.md
@@ -3,13 +3,14 @@
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)
+[v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)
[![GoDoc](https://godoc.org/github.com/pelletier/go-toml?status.svg)](http://godoc.org/github.com/pelletier/go-toml)
[![license](https://img.shields.io/github/license/pelletier/go-toml.svg)](https://github.com/pelletier/go-toml/blob/master/LICENSE)
-[![Build Status](https://travis-ci.org/pelletier/go-toml.svg?branch=master)](https://travis-ci.org/pelletier/go-toml)
-[![Coverage Status](https://coveralls.io/repos/github/pelletier/go-toml/badge.svg?branch=master)](https://coveralls.io/github/pelletier/go-toml?branch=master)
+[![Build Status](https://dev.azure.com/pelletierthomas/go-toml-ci/_apis/build/status/pelletier.go-toml?branchName=master)](https://dev.azure.com/pelletierthomas/go-toml-ci/_build/latest?definitionId=1&branchName=master)
+[![codecov](https://codecov.io/gh/pelletier/go-toml/branch/master/graph/badge.svg)](https://codecov.io/gh/pelletier/go-toml)
[![Go Report Card](https://goreportcard.com/badge/github.com/pelletier/go-toml)](https://goreportcard.com/report/github.com/pelletier/go-toml)
+[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield)
## Features
@@ -98,6 +99,30 @@ Go-toml provides two handy command line tools:
go install github.com/pelletier/go-toml/cmd/tomljson
tomljson --help
```
+
+ * `jsontoml`: Reads a JSON file and outputs a TOML representation.
+
+ ```
+ go install github.com/pelletier/go-toml/cmd/jsontoml
+ jsontoml --help
+ ```
+
+### Docker image
+
+Those tools are also availble as a Docker image from
+[dockerhub](https://hub.docker.com/r/pelletier/go-toml). For example, to
+use `tomljson`:
+
+```
+docker run -v $PWD:/workdir pelletier/go-toml tomljson /workdir/example.toml
+```
+
+Only master (`latest`) and tagged versions are published to dockerhub. You
+can build your own image as usual:
+
+```
+docker build -t go-toml .
+```
## Contribute
@@ -107,12 +132,7 @@ much appreciated!
### Run tests
-You have to make sure two kind of tests run:
-
-1. The Go unit tests
-2. The TOML examples base
-
-You can run both of them using `./test.sh`.
+`go test ./...`
### Fuzzing
diff --git a/vendor/github.com/pelletier/go-toml/azure-pipelines.yml b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml
new file mode 100644
index 00000000..242b5b54
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml
@@ -0,0 +1,230 @@
+trigger:
+- master
+
+stages:
+- stage: fuzzit
+ displayName: "Run Fuzzit"
+ dependsOn: []
+ condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master'))
+ jobs:
+ - job: submit
+ displayName: "Submit"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go 1.14"
+ inputs:
+ version: "1.14"
+ - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/"
+ - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml
+ - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml
+ - task: Bash@3
+ inputs:
+ filePath: './fuzzit.sh'
+ env:
+ TYPE: fuzzing
+ FUZZIT_API_KEY: $(FUZZIT_API_KEY)
+
+- stage: run_checks
+ displayName: "Check"
+ dependsOn: []
+ jobs:
+ - job: fmt
+ displayName: "fmt"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go 1.14"
+ inputs:
+ version: "1.14"
+ - task: Go@0
+ displayName: "go fmt ./..."
+ inputs:
+ command: 'custom'
+ customCommand: 'fmt'
+ arguments: './...'
+ - job: coverage
+ displayName: "coverage"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go 1.14"
+ inputs:
+ version: "1.14"
+ - task: Go@0
+ displayName: "Generate coverage"
+ inputs:
+ command: 'test'
+ arguments: "-race -coverprofile=coverage.txt -covermode=atomic"
+ - task: Bash@3
+ inputs:
+ targetType: 'inline'
+ script: 'bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}'
+ env:
+ CODECOV_TOKEN: $(CODECOV_TOKEN)
+ - job: benchmark
+ displayName: "benchmark"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go 1.14"
+ inputs:
+ version: "1.14"
+ - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/"
+ - task: Bash@3
+ inputs:
+ filePath: './benchmark.sh'
+ arguments: "master $(Build.Repository.Uri)"
+
+ - job: fuzzing
+ displayName: "fuzzing"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go 1.14"
+ inputs:
+ version: "1.14"
+ - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/"
+ - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml
+ - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml
+ - task: Bash@3
+ inputs:
+ filePath: './fuzzit.sh'
+ env:
+ TYPE: local-regression
+
+ - job: go_unit_tests
+ displayName: "unit tests"
+ strategy:
+ matrix:
+ linux 1.14:
+ goVersion: '1.14'
+ imageName: 'ubuntu-latest'
+ mac 1.14:
+ goVersion: '1.14'
+ imageName: 'macOS-latest'
+ windows 1.14:
+ goVersion: '1.14'
+ imageName: 'windows-latest'
+ linux 1.13:
+ goVersion: '1.13'
+ imageName: 'ubuntu-latest'
+ mac 1.13:
+ goVersion: '1.13'
+ imageName: 'macOS-latest'
+ windows 1.13:
+ goVersion: '1.13'
+ imageName: 'windows-latest'
+ pool:
+ vmImage: $(imageName)
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go $(goVersion)"
+ inputs:
+ version: $(goVersion)
+ - task: Go@0
+ displayName: "go test ./..."
+ inputs:
+ command: 'test'
+ arguments: './...'
+- stage: build_binaries
+ displayName: "Build binaries"
+ dependsOn: run_checks
+ jobs:
+ - job: build_binary
+ displayName: "Build binary"
+ strategy:
+ matrix:
+ linux_amd64:
+ GOOS: linux
+ GOARCH: amd64
+ darwin_amd64:
+ GOOS: darwin
+ GOARCH: amd64
+ windows_amd64:
+ GOOS: windows
+ GOARCH: amd64
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: GoTool@0
+ displayName: "Install Go"
+ inputs:
+ version: 1.14
+ - task: Bash@3
+ inputs:
+ targetType: inline
+ script: "make dist"
+ env:
+ go.goos: $(GOOS)
+ go.goarch: $(GOARCH)
+ - task: CopyFiles@2
+ inputs:
+ sourceFolder: '$(Build.SourcesDirectory)'
+ contents: '*.tar.xz'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)'
+ - task: PublishBuildArtifacts@1
+ inputs:
+ pathtoPublish: '$(Build.ArtifactStagingDirectory)'
+ artifactName: binaries
+- stage: build_binaries_manifest
+ displayName: "Build binaries manifest"
+ dependsOn: build_binaries
+ jobs:
+ - job: build_manifest
+ displayName: "Build binaries manifest"
+ steps:
+ - task: DownloadBuildArtifacts@0
+ inputs:
+ buildType: 'current'
+ downloadType: 'single'
+ artifactName: 'binaries'
+ downloadPath: '$(Build.SourcesDirectory)'
+ - task: Bash@3
+ inputs:
+ targetType: inline
+ script: "cd binaries && sha256sum --binary *.tar.xz | tee $(Build.ArtifactStagingDirectory)/sha256sums.txt"
+ - task: PublishBuildArtifacts@1
+ inputs:
+ pathtoPublish: '$(Build.ArtifactStagingDirectory)'
+ artifactName: manifest
+
+- stage: build_docker_image
+ displayName: "Build Docker image"
+ dependsOn: run_checks
+ jobs:
+ - job: build
+ displayName: "Build"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: Docker@2
+ inputs:
+ command: 'build'
+ Dockerfile: 'Dockerfile'
+ buildContext: '.'
+ addPipelineData: false
+
+- stage: publish_docker_image
+ displayName: "Publish Docker image"
+ dependsOn: build_docker_image
+ condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master'))
+ jobs:
+ - job: publish
+ displayName: "Publish"
+ pool:
+ vmImage: ubuntu-latest
+ steps:
+ - task: Docker@2
+ inputs:
+ containerRegistry: 'DockerHub'
+ repository: 'pelletier/go-toml'
+ command: 'buildAndPush'
+ Dockerfile: 'Dockerfile'
+ buildContext: '.'
+ tags: 'latest'
diff --git a/vendor/github.com/pelletier/go-toml/benchmark.sh b/vendor/github.com/pelletier/go-toml/benchmark.sh
index 8b8bb528..7914fff4 100644
--- a/vendor/github.com/pelletier/go-toml/benchmark.sh
+++ b/vendor/github.com/pelletier/go-toml/benchmark.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-set -e
+set -ex
reference_ref=${1:-master}
reference_git=${2:-.}
@@ -8,7 +8,6 @@ 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`
@@ -29,4 +28,4 @@ go test -bench=. -benchmem | tee ${local_benchmark}
echo ""
echo "=== diff"
-benchstat -delta-test=none ${ref_benchmark} ${local_benchmark} \ No newline at end of file
+benchstat -delta-test=none ${ref_benchmark} ${local_benchmark}
diff --git a/vendor/github.com/pelletier/go-toml/doc.go b/vendor/github.com/pelletier/go-toml/doc.go
index d5fd98c0..a1406a32 100644
--- a/vendor/github.com/pelletier/go-toml/doc.go
+++ b/vendor/github.com/pelletier/go-toml/doc.go
@@ -1,7 +1,7 @@
// 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
+// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
//
// Marshaling
//
diff --git a/vendor/github.com/pelletier/go-toml/fuzzit.sh b/vendor/github.com/pelletier/go-toml/fuzzit.sh
new file mode 100644
index 00000000..b575a608
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/fuzzit.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+set -xe
+
+# go-fuzz doesn't support modules yet, so ensure we do everything
+# in the old style GOPATH way
+export GO111MODULE="off"
+
+# install go-fuzz
+go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
+
+# target name can only contain lower-case letters (a-z), digits (0-9) and a dash (-)
+# to add another target, make sure to create it with `fuzzit create target`
+# before using `fuzzit create job`
+TARGET=toml-fuzzer
+
+go-fuzz-build -libfuzzer -o ${TARGET}.a github.com/pelletier/go-toml
+clang -fsanitize=fuzzer ${TARGET}.a -o ${TARGET}
+
+# install fuzzit for talking to fuzzit.dev service
+# or latest version:
+# https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_Linux_x86_64
+wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.52/fuzzit_Linux_x86_64
+chmod a+x fuzzit
+
+# TODO: change kkowalczyk to go-toml and create toml-fuzzer target there
+./fuzzit create job --type $TYPE go-toml/${TARGET} ${TARGET}
diff --git a/vendor/github.com/pelletier/go-toml/go.mod b/vendor/github.com/pelletier/go-toml/go.mod
new file mode 100644
index 00000000..07a258be
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/go.mod
@@ -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.8
+)
diff --git a/vendor/github.com/pelletier/go-toml/go.sum b/vendor/github.com/pelletier/go-toml/go.sum
new file mode 100644
index 00000000..b30bc766
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/go.sum
@@ -0,0 +1,17 @@
+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=
+gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/pelletier/go-toml/keysparsing.go b/vendor/github.com/pelletier/go-toml/keysparsing.go
index 284db646..e923bc4f 100644
--- a/vendor/github.com/pelletier/go-toml/keysparsing.go
+++ b/vendor/github.com/pelletier/go-toml/keysparsing.go
@@ -3,79 +3,107 @@
package toml
import (
- "bytes"
"errors"
"fmt"
"unicode"
)
// Convert the bare key group string to an array.
-// The input supports double quotation to allow "." inside the key name,
+// 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) {
- groups := []string{}
- var buffer bytes.Buffer
- inQuotes := false
- wasInQuotes := false
- ignoreSpace := true
- expectDot := false
+ runes := []rune(key)
+ var groups []string
- for _, char := range key {
- if ignoreSpace {
- if char == ' ' {
- continue
- }
- ignoreSpace = false
+ 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
}
- switch char {
- case '"':
- if inQuotes {
- groups = append(groups, buffer.String())
- buffer.Reset()
- wasInQuotes = true
- }
- inQuotes = !inQuotes
- expectDot = false
- case '.':
- if inQuotes {
- buffer.WriteRune(char)
- } else {
- if !wasInQuotes {
- if buffer.Len() == 0 {
- return nil, errors.New("empty table key")
+ 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
}
- groups = append(groups, buffer.String())
- buffer.Reset()
+ 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")
}
- ignoreSpace = true
- expectDot = false
- wasInQuotes = false
+ r = runes[idx]
+ if r == '\'' {
+ groups = append(groups, string(runes[startIdx:idx]))
+ idx++
+ break
+ }
+ idx++
}
- case ' ':
- if inQuotes {
- buffer.WriteRune(char)
- } else {
- expectDot = true
+ } 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++
}
- default:
- if !inQuotes && !isValidBareChar(char) {
- return nil, fmt.Errorf("invalid bare character: %c", char)
+ } else if r == '.' {
+ idx++
+ if idx >= len(runes) {
+ return nil, fmt.Errorf("unexpected end of key")
}
- if !inQuotes && expectDot {
- return nil, errors.New("what?")
+ r = runes[idx]
+ if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' {
+ return nil, fmt.Errorf("expecting key part after dot")
}
- buffer.WriteRune(char)
- expectDot = false
+ } else {
+ return nil, fmt.Errorf("invalid key character: %c", r)
}
}
- if inQuotes {
- return nil, errors.New("mismatched quotes")
- }
- if buffer.Len() > 0 {
- groups = append(groups, buffer.String())
- }
if len(groups) == 0 {
- return nil, errors.New("empty key")
+ return nil, fmt.Errorf("empty key")
}
return groups, nil
}
diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go
index d11de428..88fd91ee 100644
--- a/vendor/github.com/pelletier/go-toml/lexer.go
+++ b/vendor/github.com/pelletier/go-toml/lexer.go
@@ -223,9 +223,12 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn {
}
possibleDate := l.peekString(35)
- dateMatch := dateRegexp.FindString(possibleDate)
- if dateMatch != "" {
- l.fastForward(len(dateMatch))
+ dateSubmatches := dateRegexp.FindStringSubmatch(possibleDate)
+ if dateSubmatches != nil && dateSubmatches[0] != "" {
+ l.fastForward(len(dateSubmatches[0]))
+ if dateSubmatches[2] == "" { // no timezone information => local date
+ return l.lexLocalDate
+ }
return l.lexDate
}
@@ -247,7 +250,7 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn {
func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn {
l.next()
l.emit(tokenLeftCurlyBrace)
- return l.lexRvalue
+ return l.lexVoid
}
func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn {
@@ -261,6 +264,11 @@ func (l *tomlLexer) lexDate() tomlLexStateFn {
return l.lexRvalue
}
+func (l *tomlLexer) lexLocalDate() tomlLexStateFn {
+ l.emit(tokenLocalDate)
+ return l.lexRvalue
+}
+
func (l *tomlLexer) lexTrue() tomlLexStateFn {
l.fastForward(4)
l.emit(tokenTrue)
@@ -309,7 +317,7 @@ func (l *tomlLexer) lexKey() tomlLexStateFn {
if err != nil {
return l.errorf(err.Error())
}
- growingString += str
+ growingString += "\"" + str + "\""
l.next()
continue
} else if r == '\'' {
@@ -318,13 +326,15 @@ func (l *tomlLexer) lexKey() tomlLexStateFn {
if err != nil {
return l.errorf(err.Error())
}
- growingString += str
+ 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)
}
@@ -731,7 +741,27 @@ func (l *tomlLexer) run() {
}
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})`)
+ // Regexp for all date/time formats supported by TOML.
+ // Group 1: nano precision
+ // Group 2: timezone
+ //
+ // /!\ also matches the empty string
+ //
+ // Example matches:
+ //1979-05-27T07:32:00Z
+ //1979-05-27T00:32:00-07:00
+ //1979-05-27T00:32:00.999999-07:00
+ //1979-05-27 07:32:00Z
+ //1979-05-27 00:32:00-07:00
+ //1979-05-27 00:32:00.999999-07:00
+ //1979-05-27T07:32:00
+ //1979-05-27T00:32:00.999999
+ //1979-05-27 07:32:00
+ //1979-05-27 00:32:00.999999
+ //1979-05-27
+ //07:32:00
+ //00:32:00.999999
+ dateRegexp = regexp.MustCompile(`^(?:\d{1,4}-\d{2}-\d{2})?(?:[T ]?\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?)?`)
}
// Entry point
diff --git a/vendor/github.com/pelletier/go-toml/localtime.go b/vendor/github.com/pelletier/go-toml/localtime.go
new file mode 100644
index 00000000..a2149e96
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/localtime.go
@@ -0,0 +1,281 @@
+// Implementation of TOML's local date/time.
+// Copied over from https://github.com/googleapis/google-cloud-go/blob/master/civil/civil.go
+// to avoid pulling all the Google dependencies.
+//
+// Copyright 2016 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package civil implements types for civil time, a time-zone-independent
+// representation of time that follows the rules of the proleptic
+// Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second
+// minutes.
+//
+// Because they lack location information, these types do not represent unique
+// moments or intervals of time. Use time.Time for that purpose.
+package toml
+
+import (
+ "fmt"
+ "time"
+)
+
+// A LocalDate represents a date (year, month, day).
+//
+// This type does not include location information, and therefore does not
+// describe a unique 24-hour timespan.
+type LocalDate struct {
+ Year int // Year (e.g., 2014).
+ Month time.Month // Month of the year (January = 1, ...).
+ Day int // Day of the month, starting at 1.
+}
+
+// LocalDateOf returns the LocalDate in which a time occurs in that time's location.
+func LocalDateOf(t time.Time) LocalDate {
+ var d LocalDate
+ d.Year, d.Month, d.Day = t.Date()
+ return d
+}
+
+// ParseLocalDate parses a string in RFC3339 full-date format and returns the date value it represents.
+func ParseLocalDate(s string) (LocalDate, error) {
+ t, err := time.Parse("2006-01-02", s)
+ if err != nil {
+ return LocalDate{}, err
+ }
+ return LocalDateOf(t), nil
+}
+
+// String returns the date in RFC3339 full-date format.
+func (d LocalDate) String() string {
+ return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
+}
+
+// IsValid reports whether the date is valid.
+func (d LocalDate) IsValid() bool {
+ return LocalDateOf(d.In(time.UTC)) == d
+}
+
+// In returns the time corresponding to time 00:00:00 of the date in the location.
+//
+// In is always consistent with time.LocalDate, even when time.LocalDate returns a time
+// on a different day. For example, if loc is America/Indiana/Vincennes, then both
+// time.LocalDate(1955, time.May, 1, 0, 0, 0, 0, loc)
+// and
+// civil.LocalDate{Year: 1955, Month: time.May, Day: 1}.In(loc)
+// return 23:00:00 on April 30, 1955.
+//
+// In panics if loc is nil.
+func (d LocalDate) In(loc *time.Location) time.Time {
+ return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc)
+}
+
+// AddDays returns the date that is n days in the future.
+// n can also be negative to go into the past.
+func (d LocalDate) AddDays(n int) LocalDate {
+ return LocalDateOf(d.In(time.UTC).AddDate(0, 0, n))
+}
+
+// DaysSince returns the signed number of days between the date and s, not including the end day.
+// This is the inverse operation to AddDays.
+func (d LocalDate) DaysSince(s LocalDate) (days int) {
+ // We convert to Unix time so we do not have to worry about leap seconds:
+ // Unix time increases by exactly 86400 seconds per day.
+ deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix()
+ return int(deltaUnix / 86400)
+}
+
+// Before reports whether d1 occurs before d2.
+func (d1 LocalDate) Before(d2 LocalDate) bool {
+ if d1.Year != d2.Year {
+ return d1.Year < d2.Year
+ }
+ if d1.Month != d2.Month {
+ return d1.Month < d2.Month
+ }
+ return d1.Day < d2.Day
+}
+
+// After reports whether d1 occurs after d2.
+func (d1 LocalDate) After(d2 LocalDate) bool {
+ return d2.Before(d1)
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+// The output is the result of d.String().
+func (d LocalDate) MarshalText() ([]byte, error) {
+ return []byte(d.String()), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// The date is expected to be a string in a format accepted by ParseLocalDate.
+func (d *LocalDate) UnmarshalText(data []byte) error {
+ var err error
+ *d, err = ParseLocalDate(string(data))
+ return err
+}
+
+// A LocalTime represents a time with nanosecond precision.
+//
+// This type does not include location information, and therefore does not
+// describe a unique moment in time.
+//
+// This type exists to represent the TIME type in storage-based APIs like BigQuery.
+// Most operations on Times are unlikely to be meaningful. Prefer the LocalDateTime type.
+type LocalTime struct {
+ Hour int // The hour of the day in 24-hour format; range [0-23]
+ Minute int // The minute of the hour; range [0-59]
+ Second int // The second of the minute; range [0-59]
+ Nanosecond int // The nanosecond of the second; range [0-999999999]
+}
+
+// LocalTimeOf returns the LocalTime representing the time of day in which a time occurs
+// in that time's location. It ignores the date.
+func LocalTimeOf(t time.Time) LocalTime {
+ var tm LocalTime
+ tm.Hour, tm.Minute, tm.Second = t.Clock()
+ tm.Nanosecond = t.Nanosecond()
+ return tm
+}
+
+// ParseLocalTime parses a string and returns the time value it represents.
+// ParseLocalTime accepts an extended form of the RFC3339 partial-time format. After
+// the HH:MM:SS part of the string, an optional fractional part may appear,
+// consisting of a decimal point followed by one to nine decimal digits.
+// (RFC3339 admits only one digit after the decimal point).
+func ParseLocalTime(s string) (LocalTime, error) {
+ t, err := time.Parse("15:04:05.999999999", s)
+ if err != nil {
+ return LocalTime{}, err
+ }
+ return LocalTimeOf(t), nil
+}
+
+// String returns the date in the format described in ParseLocalTime. If Nanoseconds
+// is zero, no fractional part will be generated. Otherwise, the result will
+// end with a fractional part consisting of a decimal point and nine digits.
+func (t LocalTime) String() string {
+ s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
+ if t.Nanosecond == 0 {
+ return s
+ }
+ return s + fmt.Sprintf(".%09d", t.Nanosecond)
+}
+
+// IsValid reports whether the time is valid.
+func (t LocalTime) IsValid() bool {
+ // Construct a non-zero time.
+ tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC)
+ return LocalTimeOf(tm) == t
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+// The output is the result of t.String().
+func (t LocalTime) MarshalText() ([]byte, error) {
+ return []byte(t.String()), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// The time is expected to be a string in a format accepted by ParseLocalTime.
+func (t *LocalTime) UnmarshalText(data []byte) error {
+ var err error
+ *t, err = ParseLocalTime(string(data))
+ return err
+}
+
+// A LocalDateTime represents a date and time.
+//
+// This type does not include location information, and therefore does not
+// describe a unique moment in time.
+type LocalDateTime struct {
+ Date LocalDate
+ Time LocalTime
+}
+
+// Note: We deliberately do not embed LocalDate into LocalDateTime, to avoid promoting AddDays and Sub.
+
+// LocalDateTimeOf returns the LocalDateTime in which a time occurs in that time's location.
+func LocalDateTimeOf(t time.Time) LocalDateTime {
+ return LocalDateTime{
+ Date: LocalDateOf(t),
+ Time: LocalTimeOf(t),
+ }
+}
+
+// ParseLocalDateTime parses a string and returns the LocalDateTime it represents.
+// ParseLocalDateTime accepts a variant of the RFC3339 date-time format that omits
+// the time offset but includes an optional fractional time, as described in
+// ParseLocalTime. Informally, the accepted format is
+// YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]
+// where the 'T' may be a lower-case 't'.
+func ParseLocalDateTime(s string) (LocalDateTime, error) {
+ t, err := time.Parse("2006-01-02T15:04:05.999999999", s)
+ if err != nil {
+ t, err = time.Parse("2006-01-02t15:04:05.999999999", s)
+ if err != nil {
+ return LocalDateTime{}, err
+ }
+ }
+ return LocalDateTimeOf(t), nil
+}
+
+// String returns the date in the format described in ParseLocalDate.
+func (dt LocalDateTime) String() string {
+ return dt.Date.String() + "T" + dt.Time.String()
+}
+
+// IsValid reports whether the datetime is valid.
+func (dt LocalDateTime) IsValid() bool {
+ return dt.Date.IsValid() && dt.Time.IsValid()
+}
+
+// In returns the time corresponding to the LocalDateTime in the given location.
+//
+// If the time is missing or ambigous at the location, In returns the same
+// result as time.LocalDate. For example, if loc is America/Indiana/Vincennes, then
+// both
+// time.LocalDate(1955, time.May, 1, 0, 30, 0, 0, loc)
+// and
+// civil.LocalDateTime{
+// civil.LocalDate{Year: 1955, Month: time.May, Day: 1}},
+// civil.LocalTime{Minute: 30}}.In(loc)
+// return 23:30:00 on April 30, 1955.
+//
+// In panics if loc is nil.
+func (dt LocalDateTime) In(loc *time.Location) time.Time {
+ return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc)
+}
+
+// Before reports whether dt1 occurs before dt2.
+func (dt1 LocalDateTime) Before(dt2 LocalDateTime) bool {
+ return dt1.In(time.UTC).Before(dt2.In(time.UTC))
+}
+
+// After reports whether dt1 occurs after dt2.
+func (dt1 LocalDateTime) After(dt2 LocalDateTime) bool {
+ return dt2.Before(dt1)
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+// The output is the result of dt.String().
+func (dt LocalDateTime) MarshalText() ([]byte, error) {
+ return []byte(dt.String()), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+// The datetime is expected to be a string in a format accepted by ParseLocalDateTime
+func (dt *LocalDateTime) UnmarshalText(data []byte) error {
+ var err error
+ *dt, err = ParseLocalDateTime(string(data))
+ return err
+}
diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go
index 671da556..dcddad8d 100644
--- a/vendor/github.com/pelletier/go-toml/marshal.go
+++ b/vendor/github.com/pelletier/go-toml/marshal.go
@@ -6,20 +6,28 @@ import (
"fmt"
"io"
"reflect"
+ "sort"
"strconv"
"strings"
"time"
)
-const tagKeyMultiline = "multiline"
+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
+ name string
+ comment string
+ commented bool
+ multiline bool
+ include bool
+ omitempty bool
+ defaultValue string
}
type encOpts struct {
@@ -31,10 +39,40 @@ 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()
+var localDateType = reflect.TypeOf(LocalDate{})
+var localTimeType = reflect.TypeOf(LocalTime{})
+var localDateTimeType = reflect.TypeOf(LocalDateTime{})
-// Check if the given marshall type maps to a Tree primitive
+// Check if the given marshal type maps to a Tree primitive
func isPrimitive(mtype reflect.Type) bool {
switch mtype.Kind() {
case reflect.Ptr:
@@ -50,37 +88,41 @@ func isPrimitive(mtype reflect.Type) bool {
case reflect.String:
return true
case reflect.Struct:
- return mtype == timeType || isCustomMarshaler(mtype)
+ return mtype == timeType || mtype == localDateType || mtype == localDateTimeType || mtype == localTimeType || isCustomMarshaler(mtype)
default:
return false
}
}
-// Check if the given marshall type maps to a Tree slice
-func isTreeSlice(mtype reflect.Type) bool {
+// Check if the given marshal type maps to a Tree slice or array
+func isTreeSequence(mtype reflect.Type) bool {
switch mtype.Kind() {
- case reflect.Slice:
- return !isOtherSlice(mtype)
+ case reflect.Ptr:
+ return isTreeSequence(mtype.Elem())
+ case reflect.Slice, reflect.Array:
+ return isTree(mtype.Elem())
default:
return false
}
}
-// Check if the given marshall type maps to a non-Tree slice
-func isOtherSlice(mtype reflect.Type) bool {
+// Check if the given marshal type maps to a non-Tree slice or array
+func isOtherSequence(mtype reflect.Type) bool {
switch mtype.Kind() {
case reflect.Ptr:
- return isOtherSlice(mtype.Elem())
- case reflect.Slice:
- return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem())
+ return isOtherSequence(mtype.Elem())
+ case reflect.Slice, reflect.Array:
+ return !isTreeSequence(mtype)
default:
return false
}
}
-// Check if the given marshall type maps to a Tree
+// Check if the given marshal type maps to a Tree
func isTree(mtype reflect.Type) bool {
switch mtype.Kind() {
+ case reflect.Ptr:
+ return isTree(mtype.Elem())
case reflect.Map:
return true
case reflect.Struct:
@@ -135,7 +177,9 @@ Tree primitive types and corresponding marshal types:
float64 float32, float64, pointers to same
string string, pointers to same
bool bool, pointers to same
- time.Time time.Time{}, pointers to same
+ time.LocalTime time.LocalTime{}, pointers to same
+
+For additional flexibility, use the Encoder API.
*/
func Marshal(v interface{}) ([]byte, error) {
return NewEncoder(nil).marshal(v)
@@ -145,13 +189,21 @@ func Marshal(v interface{}) ([]byte, error) {
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,
+ w: w,
+ encOpts: encOptsDefaults,
+ annotation: annotationDefault,
+ line: 0,
+ col: 1,
+ order: OrderAlphabetical,
}
}
@@ -197,11 +249,49 @@ func (e *Encoder) ArraysWithOneElementPerLine(v bool) *Encoder {
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)
- if mtype.Kind() != reflect.Struct {
- return []byte{}, errors.New("Only a struct can be marshaled to TOML")
+
+ 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)
@@ -212,44 +302,76 @@ func (e *Encoder) marshal(v interface{}) ([]byte, error) {
}
var buf bytes.Buffer
- _, err = t.writeTo(&buf, "", "", 0, e.arraysOneElementPerLine)
+ _, err = t.writeToOrdered(&buf, "", "", 0, e.arraysOneElementPerLine, e.order, false)
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 := newTree()
+ 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)
- if opts.include && (!opts.omitempty || !isZero(mvalf)) {
- val, err := e.valueToToml(mtypef.Type, mvalf)
- if err != nil {
- return nil, err
- }
+ switch mval.Interface().(type) {
+ case Tree:
+ reflect.ValueOf(tval).Elem().Set(mval)
+ default:
+ for i := 0; i < mtype.NumField(); i++ {
+ mtypef, mvalf := mtype.Field(i), mval.Field(i)
+ opts := tomlOptions(mtypef, e.annotation)
+ if opts.include && ((mtypef.Type.Kind() != reflect.Interface && !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)
+ tval.SetWithOptions(opts.name, SetOptions{
+ Comment: opts.comment,
+ Commented: opts.commented,
+ Multiline: opts.multiline,
+ }, val)
+ }
}
}
case reflect.Map:
- for _, key := range mval.MapKeys() {
+ 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)
+ if (mtype.Elem().Kind() == reflect.Ptr || mtype.Elem().Kind() == reflect.Interface) && mvalf.IsNil() {
+ continue
+ }
val, err := e.valueToToml(mtype.Elem(), mvalf)
if err != nil {
return nil, err
}
if e.quoteMapKeys {
- keyStr, err := tomlValueStringRepresentation(key.String(), "", e.arraysOneElementPerLine)
+ keyStr, err := tomlValueStringRepresentation(key.String(), "", "", e.arraysOneElementPerLine)
if err != nil {
return nil, err
}
@@ -277,6 +399,9 @@ func (e *Encoder) valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*T
// Convert given marshal slice to slice of toml values
func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
+ if mtype.Elem().Kind() == reflect.Interface {
+ return nil, fmt.Errorf("marshal can't handle []interface{}")
+ }
tval := make([]interface{}, mval.Len(), mval.Len())
for i := 0; i < mval.Len(); i++ {
val, err := e.valueToToml(mtype.Elem(), mval.Index(i))
@@ -290,23 +415,30 @@ func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (int
// 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())
}
+ if mtype.Kind() == reflect.Interface {
+ return e.valueToToml(mval.Elem().Type(), mval.Elem())
+ }
switch {
case isCustomMarshaler(mtype):
return callCustomMarshaler(mval)
case isTree(mtype):
return e.valueToTree(mtype, mval)
- case isTreeSlice(mtype):
+ case isTreeSequence(mtype):
return e.valueToTreeSlice(mtype, mval)
- case isOtherSlice(mtype):
+ case isOtherSequence(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
@@ -315,7 +447,7 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface
case reflect.String:
return mval.String(), nil
case reflect.Struct:
- return mval.Interface().(time.Time), nil
+ return mval.Interface(), nil
default:
return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind())
}
@@ -326,7 +458,7 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface
// 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}
+ d := Decoder{tval: t, tagName: tagFieldName}
return d.unmarshal(v)
}
@@ -334,8 +466,11 @@ func (t *Tree) Unmarshal(v interface{}) error {
// 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
+ _, err := t.WriteTo(&buf)
+ if err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
}
// Unmarshal parses the TOML-encoded data and stores the result in the value
@@ -347,6 +482,14 @@ func (t *Tree) Marshal() ([]byte, error) {
// 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 {
@@ -362,6 +505,7 @@ type Decoder struct {
r io.Reader
tval *Tree
encOpts
+ tagName string
}
// NewDecoder returns a new decoder that reads from r.
@@ -369,6 +513,7 @@ func NewDecoder(r io.Reader) *Decoder {
return &Decoder{
r: r,
encOpts: encOptsDefaults,
+ tagName: tagFieldName,
}
}
@@ -385,13 +530,29 @@ func (d *Decoder) Decode(v interface{}) error {
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 || mtype.Elem().Kind() != reflect.Struct {
- return errors.New("Only a pointer to struct can be unmarshaled from TOML")
+ if mtype.Kind() != reflect.Ptr {
+ return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
}
- sval, err := d.valueFromTree(mtype.Elem(), d.tval)
+ 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")
+ }
+
+ vv := reflect.ValueOf(v).Elem()
+
+ sval, err := d.valueFromTree(elem, d.tval, &vv)
if err != nil {
return err
}
@@ -399,33 +560,103 @@ func (d *Decoder) unmarshal(v interface{}) error {
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) {
+// Convert toml tree to marshal struct or map, using marshal type. When mval1
+// is non-nil, merge fields into the given value instead of allocating a new one.
+func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.Value) (reflect.Value, error) {
if mtype.Kind() == reflect.Ptr {
- return d.unwrapPointer(mtype, tval)
+ return d.unwrapPointer(mtype, tval, mval1)
}
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)
- opts := tomlOptions(mtypef)
- if opts.include {
+ if mval1 != nil {
+ mval = *mval1
+ } else {
+ mval = reflect.New(mtype).Elem()
+ }
+
+ switch mval.Interface().(type) {
+ case Tree:
+ mval.Set(reflect.ValueOf(tval).Elem())
+ default:
+ for i := 0; i < mtype.NumField(); i++ {
+ mtypef := mtype.Field(i)
+ an := annotation{tag: d.tagName}
+ opts := tomlOptions(mtypef, an)
+ if !opts.include {
+ continue
+ }
baseKey := opts.name
- keysToTry := []string{baseKey, strings.ToLower(baseKey), strings.ToTitle(baseKey)}
- for _, key := range keysToTry {
- exists := tval.Has(key)
- if !exists {
- continue
+ keysToTry := []string{
+ baseKey,
+ strings.ToLower(baseKey),
+ strings.ToTitle(baseKey),
+ strings.ToLower(string(baseKey[0])) + baseKey[1:],
+ }
+
+ found := false
+ if tval != nil {
+ for _, key := range keysToTry {
+ exists := tval.Has(key)
+ if !exists {
+ continue
+ }
+ val := tval.Get(key)
+ fval := mval.Field(i)
+ mvalf, err := d.valueFromToml(mtypef.Type, val, &fval)
+ if err != nil {
+ return mval, formatError(err, tval.GetPosition(key))
+ }
+ mval.Field(i).Set(mvalf)
+ found = true
+ break
}
- val := tval.Get(key)
- mvalf, err := d.valueFromToml(mtypef.Type, val)
+ }
+
+ 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))
+ }
+
+ // save the old behavior above and try to check structs
+ if !found && opts.defaultValue == "" && mtypef.Type.Kind() == reflect.Struct {
+ tmpTval := tval
+ if !mtypef.Anonymous {
+ tmpTval = nil
+ }
+ v, err := d.valueFromTree(mtypef.Type, tmpTval, nil)
if err != nil {
- return mval, formatError(err, tval.GetPosition(key))
+ return v, err
}
- mval.Field(i).Set(mvalf)
- break
+ mval.Field(i).Set(v)
}
}
}
@@ -434,11 +665,11 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
for _, key := range tval.Keys() {
// TODO: path splits key
val := tval.GetPath([]string{key})
- mvalf, err := d.valueFromToml(mtype.Elem(), val)
+ mvalf, err := d.valueFromToml(mtype.Elem(), val, nil)
if err != nil {
return mval, formatError(err, tval.GetPosition(key))
}
- mval.SetMapIndex(reflect.ValueOf(key), mvalf)
+ mval.SetMapIndex(reflect.ValueOf(key).Convert(mtype.Key()), mvalf)
}
}
return mval, nil
@@ -448,7 +679,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
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])
+ val, err := d.valueFromTree(mtype.Elem(), tval[i], nil)
if err != nil {
return mval, err
}
@@ -461,7 +692,7 @@ func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.
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])
+ val, err := d.valueFromToml(mtype.Elem(), tval[i], nil)
if err != nil {
return mval, err
}
@@ -470,33 +701,88 @@ func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (r
return mval, nil
}
-// Convert toml value to marshal value, using marshal type
-func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
+// Convert toml value to marshal value, using marshal type. When mval1 is non-nil
+// and the given type is a struct value, merge fields into it.
+func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) {
if mtype.Kind() == reflect.Ptr {
- return d.unwrapPointer(mtype, tval)
+ return d.unwrapPointer(mtype, tval, mval1)
}
- switch tval.(type) {
+ switch t := tval.(type) {
case *Tree:
+ var mval11 *reflect.Value
+ if mtype.Kind() == reflect.Struct {
+ mval11 = mval1
+ }
+
if isTree(mtype) {
- return d.valueFromTree(mtype, tval.(*Tree))
+ return d.valueFromTree(mtype, t, mval11)
}
+
+ if mtype.Kind() == reflect.Interface {
+ if mval1 == nil || mval1.IsNil() {
+ return d.valueFromTree(reflect.TypeOf(map[string]interface{}{}), t, nil)
+ } else {
+ return d.valueFromToml(mval1.Elem().Type(), t, nil)
+ }
+ }
+
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, tval.([]*Tree))
+ if isTreeSequence(mtype) {
+ return d.valueFromTreeSlice(mtype, t)
+ }
+ if mtype.Kind() == reflect.Interface {
+ if mval1 == nil || mval1.IsNil() {
+ return d.valueFromTreeSlice(reflect.TypeOf([]map[string]interface{}{}), t)
+ } else {
+ ival := mval1.Elem()
+ return d.valueFromToml(mval1.Elem().Type(), t, &ival)
+ }
}
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to trees", tval, tval)
case []interface{}:
- if isOtherSlice(mtype) {
- return d.valueFromOtherSlice(mtype, tval.([]interface{}))
+ if isOtherSequence(mtype) {
+ return d.valueFromOtherSlice(mtype, t)
+ }
+ if mtype.Kind() == reflect.Interface {
+ if mval1 == nil || mval1.IsNil() {
+ return d.valueFromOtherSlice(reflect.TypeOf([]interface{}{}), t)
+ } else {
+ ival := mval1.Elem()
+ return d.valueFromToml(mval1.Elem().Type(), t, &ival)
+ }
}
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
+
+ switch val.Type() {
+ case localDateType:
+ localDate := val.Interface().(LocalDate)
+ switch mtype {
+ case timeType:
+ return reflect.ValueOf(time.Date(localDate.Year, localDate.Month, localDate.Day, 0, 0, 0, 0, time.Local)), nil
+ }
+ case localDateTimeType:
+ localDateTime := val.Interface().(LocalDateTime)
+ switch mtype {
+ case timeType:
+ return reflect.ValueOf(time.Date(
+ localDateTime.Date.Year,
+ localDateTime.Date.Month,
+ localDateTime.Date.Day,
+ localDateTime.Time.Hour,
+ localDateTime.Time.Minute,
+ localDateTime.Time.Second,
+ localDateTime.Time.Nanosecond,
+ time.Local)), nil
+ }
+ }
+
+ // if this passes for when mtype is reflect.Struct, tval is a time.LocalTime
if !val.Type().ConvertibleTo(mtype) {
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
}
@@ -512,10 +798,17 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V
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.Int()) {
+ 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())
}
@@ -525,10 +818,11 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V
if !val.Type().ConvertibleTo(mtype) {
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to %v", tval, tval, mtype.String())
}
- if val.Int() < 0 {
+
+ 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.Int())) {
+ 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())
}
@@ -538,19 +832,33 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V
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.Float()) {
+ 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
+ case reflect.Interface:
+ if mval1 == nil || mval1.IsNil() {
+ return reflect.ValueOf(tval), nil
+ } else {
+ ival := mval1.Elem()
+ return d.valueFromToml(mval1.Elem().Type(), t, &ival)
+ }
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)
+func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) {
+ var melem *reflect.Value
+
+ if mval1 != nil && !mval1.IsNil() && (mtype.Elem().Kind() == reflect.Struct || mtype.Elem().Kind() == reflect.Interface) {
+ elem := mval1.Elem()
+ melem = &elem
+ }
+
+ val, err := d.valueFromToml(mtype.Elem(), tval, melem)
if err != nil {
return reflect.ValueOf(nil), err
}
@@ -559,16 +867,25 @@ func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.V
return mval, nil
}
-func tomlOptions(vf reflect.StructField) tomlOpts {
- tag := vf.Tag.Get("toml")
+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("comment"); c != "" {
+ if c := vf.Tag.Get(an.comment); c != "" {
comment = c
}
- commented, _ := strconv.ParseBool(vf.Tag.Get("commented"))
- multiline, _ := strconv.ParseBool(vf.Tag.Get(tagKeyMultiline))
- result := tomlOpts{name: vf.Name, comment: comment, commented: commented, multiline: multiline, include: true, omitempty: false}
+ 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
diff --git a/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml b/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
new file mode 100644
index 00000000..792b72ed
--- /dev/null
+++ b/vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
@@ -0,0 +1,39 @@
+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
+ float64 = 123.456782132399
+ int = 5000
+ string = "Bite me"
+ date = 1979-05-27T07:32:00Z
+
+[[subdoclist]]
+ name = "List.First"
+
+[[subdoclist]]
+ name = "List.Second"
diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.toml b/vendor/github.com/pelletier/go-toml/marshal_test.toml
index 1c5f98e7..ba5e110b 100644
--- a/vendor/github.com/pelletier/go-toml/marshal_test.toml
+++ b/vendor/github.com/pelletier/go-toml/marshal_test.toml
@@ -4,6 +4,7 @@ title = "TOML Marshal Testing"
bool = true
date = 1979-05-27T07:32:00Z
float = 123.4
+ float64 = 123.456782132399
int = 5000
string = "Bite me"
uint = 5001
diff --git a/vendor/github.com/pelletier/go-toml/parser.go b/vendor/github.com/pelletier/go-toml/parser.go
index 2d27599a..1b344fee 100644
--- a/vendor/github.com/pelletier/go-toml/parser.go
+++ b/vendor/github.com/pelletier/go-toml/parser.go
@@ -77,8 +77,10 @@ func (p *tomlParser) parseStart() tomlParserStateFn {
return p.parseAssign
case tokenEOF:
return nil
+ case tokenError:
+ p.raiseError(tok, "parsing error: %s", tok.String())
default:
- p.raiseError(tok, "unexpected token")
+ p.raiseError(tok, "unexpected token %s", tok.typ)
}
return nil
}
@@ -165,6 +167,11 @@ 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 {
@@ -173,6 +180,9 @@ func (p *tomlParser) parseAssign() tomlParserStateFn {
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) {
@@ -180,17 +190,19 @@ func (p *tomlParser) parseAssign() tomlParserStateFn {
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
- keyVals := []string{key.val}
- if len(keyVals) != 1 {
- p.raiseError(key, "Invalid key")
- }
- keyVal := keyVals[0]
+ keyVal := parsedKey[len(parsedKey)-1]
localKey := []string{keyVal}
finalKey := append(tableKey, keyVal)
if targetNode.GetPath(localKey) != nil {
@@ -301,7 +313,41 @@ func (p *tomlParser) parseRvalue() interface{} {
}
return val
case tokenDate:
- val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC)
+ layout := time.RFC3339Nano
+ if !strings.Contains(tok.val, "T") {
+ layout = strings.Replace(layout, "T", " ", 1)
+ }
+ val, err := time.ParseInLocation(layout, tok.val, time.UTC)
+ if err != nil {
+ p.raiseError(tok, "%s", err)
+ }
+ return val
+ case tokenLocalDate:
+ v := strings.Replace(tok.val, " ", "T", -1)
+ isDateTime := false
+ isTime := false
+ for _, c := range v {
+ if c == 'T' || c == 't' {
+ isDateTime = true
+ break
+ }
+ if c == ':' {
+ isTime = true
+ break
+ }
+ }
+
+ var val interface{}
+ var err error
+
+ if isDateTime {
+ val, err = ParseLocalDateTime(v)
+ } else if isTime {
+ val, err = ParseLocalTime(v)
+ } else {
+ val, err = ParseLocalDate(v)
+ }
+
if err != nil {
p.raiseError(tok, "%s", err)
}
@@ -338,18 +384,21 @@ Loop:
case tokenRightCurlyBrace:
p.getToken()
break Loop
- case tokenKey:
+ case tokenKey, tokenInteger, tokenString:
if !tokenIsComma(previous) && previous != nil {
p.raiseError(follow, "comma expected between fields in inline table")
}
key := p.getToken()
p.assume(tokenEqual)
+
+ parsedKey, err := parseKey(key.val)
+ if err != nil {
+ p.raiseError(key, "invalid key: %s", err)
+ }
+
value := p.parseRvalue()
- tree.Set(key.val, value)
+ tree.SetPath(parsedKey, 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")
}
diff --git a/vendor/github.com/pelletier/go-toml/test.sh b/vendor/github.com/pelletier/go-toml/test.sh
deleted file mode 100644
index ba6adf3f..00000000
--- a/vendor/github.com/pelletier/go-toml/test.sh
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-# fail out of the script if anything here fails
-set -e
-set -o pipefail
-
-# set the path to the present working directory
-export GOPATH=`pwd`
-
-function git_clone() {
- path=$1
- branch=$2
- version=$3
- if [ ! -d "src/$path" ]; then
- mkdir -p src/$path
- git clone https://$path.git src/$path
- fi
- pushd src/$path
- git checkout "$branch"
- git reset --hard "$version"
- popd
-}
-
-# Remove potential previous runs
-rm -rf src test_program_bin toml-test
-
-go get github.com/pelletier/go-buffruneio
-go get github.com/davecgh/go-spew/spew
-go get gopkg.in/yaml.v2
-go get github.com/BurntSushi/toml
-
-# get code for BurntSushi TOML validation
-# pinning all to 'HEAD' for version 0.3.x work (TODO: pin to commit hash when tests stabilize)
-git_clone github.com/BurntSushi/toml master HEAD
-git_clone github.com/BurntSushi/toml-test master HEAD #was: 0.2.0 HEAD
-
-# build the BurntSushi test application
-go build -o toml-test github.com/BurntSushi/toml-test
-
-# vendorize the current lib for testing
-# NOTE: this basically mocks an install without having to go back out to github for code
-mkdir -p src/github.com/pelletier/go-toml/cmd
-mkdir -p src/github.com/pelletier/go-toml/query
-cp *.go *.toml src/github.com/pelletier/go-toml
-cp -R cmd/* src/github.com/pelletier/go-toml/cmd
-cp -R query/* src/github.com/pelletier/go-toml/query
-go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go
-
-# Run basic unit tests
-go test github.com/pelletier/go-toml -covermode=count -coverprofile=coverage.out
-go test github.com/pelletier/go-toml/cmd/tomljson
-go test github.com/pelletier/go-toml/query
-
-# run the entire BurntSushi test suite
-if [[ $# -eq 0 ]] ; then
- echo "Running all BurntSushi tests"
- ./toml-test ./test_program_bin | tee test_out
-else
- # run a specific test
- test=$1
- test_path='src/github.com/BurntSushi/toml-test/tests'
- valid_test="$test_path/valid/$test"
- invalid_test="$test_path/invalid/$test"
-
- if [ -e "$valid_test.toml" ]; then
- echo "Valid Test TOML for $test:"
- echo "===="
- cat "$valid_test.toml"
-
- echo "Valid Test JSON for $test:"
- echo "===="
- cat "$valid_test.json"
-
- echo "Go-TOML Output for $test:"
- echo "===="
- cat "$valid_test.toml" | ./test_program_bin
- fi
-
- if [ -e "$invalid_test.toml" ]; then
- echo "Invalid Test TOML for $test:"
- echo "===="
- cat "$invalid_test.toml"
-
- echo "Go-TOML Output for $test:"
- echo "===="
- echo "go-toml Output:"
- cat "$invalid_test.toml" | ./test_program_bin
- fi
-fi
diff --git a/vendor/github.com/pelletier/go-toml/token.go b/vendor/github.com/pelletier/go-toml/token.go
index 1a908134..36a3fc88 100644
--- a/vendor/github.com/pelletier/go-toml/token.go
+++ b/vendor/github.com/pelletier/go-toml/token.go
@@ -2,7 +2,6 @@ package toml
import (
"fmt"
- "strconv"
"unicode"
)
@@ -35,6 +34,7 @@ const (
tokenDoubleLeftBracket
tokenDoubleRightBracket
tokenDate
+ tokenLocalDate
tokenKeyGroup
tokenKeyGroupArray
tokenComma
@@ -68,7 +68,8 @@ var tokenTypeNames = []string{
")",
"]]",
"[[",
- "Date",
+ "LocalDate",
+ "LocalDate",
"KeyGroup",
"KeyGroupArray",
",",
@@ -95,14 +96,6 @@ func (tt tokenType) String() string {
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:
diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go
index 98c185ad..f4d56870 100644
--- a/vendor/github.com/pelletier/go-toml/toml.go
+++ b/vendor/github.com/pelletier/go-toml/toml.go
@@ -27,9 +27,13 @@ type Tree struct {
}
func newTree() *Tree {
+ return newTreeWithPosition(Position{})
+}
+
+func newTreeWithPosition(pos Position) *Tree {
return &Tree{
values: make(map[string]interface{}),
- position: Position{},
+ position: pos,
}
}
@@ -194,10 +198,10 @@ func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
// formatting instructions to the key, that will be reused by Marshal().
func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
subtree := t
- for _, intermediateKey := range keys[:len(keys)-1] {
+ for i, intermediateKey := range keys[:len(keys)-1] {
nextTree, exists := subtree.values[intermediateKey]
if !exists {
- nextTree = newTree()
+ nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
subtree.values[intermediateKey] = nextTree // add new element here
}
switch node := nextTree.(type) {
@@ -207,7 +211,7 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac
// go to most recent element
if len(node) == 0 {
// create element if it does not exist
- subtree.values[intermediateKey] = append(node, newTree())
+ subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
}
subtree = node[len(node)-1]
}
@@ -215,19 +219,25 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac
var toInsert interface{}
- switch value.(type) {
+ switch v := value.(type) {
case *Tree:
- tt := value.(*Tree)
- tt.comment = opts.Comment
+ v.comment = opts.Comment
+ v.commented = opts.Commented
toInsert = value
case []*Tree:
+ for i := range v {
+ v[i].commented = opts.Commented
+ }
toInsert = value
case *tomlValue:
- tt := value.(*tomlValue)
- tt.comment = opts.Comment
- toInsert = tt
+ v.comment = opts.Comment
+ toInsert = v
default:
- toInsert = &tomlValue{value: value, comment: opts.Comment, commented: opts.Commented, multiline: opts.Multiline}
+ 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
@@ -256,44 +266,35 @@ func (t *Tree) SetPath(keys []string, value interface{}) {
// 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{}) {
- subtree := t
- for _, intermediateKey := range keys[:len(keys)-1] {
- nextTree, exists := subtree.values[intermediateKey]
- if !exists {
- nextTree = newTree()
- 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, newTree())
- }
- subtree = node[len(node)-1]
- }
- }
+ t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
+}
- var toInsert interface{}
+// 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)
+}
- switch value.(type) {
+// 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:
- tt := value.(*Tree)
- tt.comment = comment
- toInsert = value
- case []*Tree:
- toInsert = value
- case *tomlValue:
- tt := value.(*tomlValue)
- tt.comment = comment
- toInsert = tt
- default:
- toInsert = &tomlValue{value: value, comment: comment, commented: commented}
+ delete(node.values, item)
+ return nil
}
-
- subtree.values[keys[len(keys)-1]] = toInsert
+ return errors.New("no such key to delete")
}
// createSubTree takes a tree and a key and create the necessary intermediate
@@ -305,10 +306,10 @@ func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool,
// Returns nil on success, error object on failure
func (t *Tree) createSubTree(keys []string, pos Position) error {
subtree := t
- for _, intermediateKey := range keys {
+ for i, intermediateKey := range keys {
nextTree, exists := subtree.values[intermediateKey]
if !exists {
- tree := newTree()
+ tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
tree.position = pos
subtree.values[intermediateKey] = tree
nextTree = tree
@@ -337,10 +338,39 @@ func LoadBytes(b []byte) (tree *Tree, err error) {
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)
diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go
index e4049e29..16c19867 100644
--- a/vendor/github.com/pelletier/go-toml/tomltree_write.go
+++ b/vendor/github.com/pelletier/go-toml/tomltree_write.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"math"
+ "math/big"
"reflect"
"sort"
"strconv"
@@ -12,12 +13,25 @@ import (
"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 {
+func encodeMultilineTomlString(value string, commented string) string {
var b bytes.Buffer
+ b.WriteString(commented)
for _, rr := range value {
switch rr {
case '\b':
@@ -25,7 +39,7 @@ func encodeMultilineTomlString(value string) string {
case '\t':
b.WriteString("\t")
case '\n':
- b.WriteString("\n")
+ b.WriteString("\n" + commented)
case '\f':
b.WriteString(`\f`)
case '\r':
@@ -78,7 +92,7 @@ func encodeTomlString(value string) string {
return b.String()
}
-func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElementPerLine bool) (string, error) {
+func tomlValueStringRepresentation(v interface{}, commented string, 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)
@@ -94,20 +108,28 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
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.
+ // Default bit length is full 64
+ bits := 64
+ // Float panics if nan is used
+ if !math.IsNaN(value) {
+ // if 32 bit accuracy is enough to exactly show, use 32
+ _, acc := big.NewFloat(value).Float32()
+ if acc == big.Exact {
+ bits = 32
+ }
+ }
if math.Trunc(value) == value {
- return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil
+ return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil
}
- return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil
+ return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil
case string:
if tv.multiline {
- return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
+ return "\"\"\"\n" + encodeMultilineTomlString(value, commented) + "\"\"\"", nil
}
return "\"" + encodeTomlString(value) + "\"", nil
case []byte:
b, _ := v.([]byte)
- return tomlValueStringRepresentation(string(b), indent, arraysOneElementPerLine)
+ return tomlValueStringRepresentation(string(b), commented, indent, arraysOneElementPerLine)
case bool:
if value {
return "true", nil
@@ -115,6 +137,12 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
return "false", nil
case time.Time:
return value.Format(time.RFC3339), nil
+ case LocalDate:
+ return value.String(), nil
+ case LocalDateTime:
+ return value.String(), nil
+ case LocalTime:
+ return value.String(), nil
case nil:
return "", nil
}
@@ -125,7 +153,7 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
var values []string
for i := 0; i < rv.Len(); i++ {
item := rv.Index(i).Interface()
- itemRepr, err := tomlValueStringRepresentation(item, indent, arraysOneElementPerLine)
+ itemRepr, err := tomlValueStringRepresentation(item, commented, indent, arraysOneElementPerLine)
if err != nil {
return "", err
}
@@ -139,12 +167,12 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
for _, value := range values {
stringBuffer.WriteString(valueIndent)
- stringBuffer.WriteString(value)
+ stringBuffer.WriteString(commented + value)
stringBuffer.WriteString(`,`)
stringBuffer.WriteString("\n")
}
- stringBuffer.WriteString(indent + "]")
+ stringBuffer.WriteString(indent + commented + "]")
return stringBuffer.String(), nil
}
@@ -153,117 +181,238 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
return "", fmt.Errorf("unsupported value type %T: %v", v, v)
}
-func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
- simpleValuesKeys := make([]string, 0)
- complexValuesKeys := make([]string, 0)
+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, []*Tree:
- complexValuesKeys = append(complexValuesKeys, k)
+ 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:
- simpleValuesKeys = append(simpleValuesKeys, k)
+ 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)
- sort.Strings(simpleValuesKeys)
- sort.Strings(complexValuesKeys)
+ for i, line := range lines {
+ vals[i] = m[line]
+ }
- for _, k := range simpleValuesKeys {
- v, ok := t.values[k].(*tomlValue)
- if !ok {
- return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
- }
+ return vals
+}
- repr, err := tomlValueStringRepresentation(v, indent, arraysOneElementPerLine)
- if err != nil {
- return bytesCount, err
- }
+func sortAlphabetical(t *Tree) (vals []sortNode) {
+ var (
+ node sortNode
+ simpVals []string
+ compVals []string
+ )
+ vals = make([]sortNode, 0)
+ m := make(map[string]sortNode)
- 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
- }
+ 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
+ }
- 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
- }
+ // Simples first to match previous implementation
+ sort.Strings(simpVals)
+ i := 0
+ for _, key := range simpVals {
+ vals[i] = m[key]
+ i++
}
- for _, k := range complexValuesKeys {
- v := t.values[k]
+ sort.Strings(compVals)
+ for _, key := range compVals {
+ vals[i] = m[key]
+ i++
+ }
- combinedKey := k
- if keyspace != "" {
- combinedKey = keyspace + "." + combinedKey
- }
- var commented string
- if t.commented {
- commented = "# "
- }
+ return vals
+}
- 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)
+func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
+ return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical, false)
+}
+
+func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord marshalOrder, parentCommented bool) (int64, error) {
+ var orderedVals []sortNode
+
+ switch ord {
+ case OrderPreserve:
+ orderedVals = sortByLines(t)
+ default:
+ orderedVals = sortAlphabetical(t)
+ }
+
+ for _, node := range orderedVals {
+ switch node.complexity {
+ case valueComplex:
+ k := node.key
+ v := t.values[k]
+
+ combinedKey := k
+ if keyspace != "" {
+ combinedKey = keyspace + "." + combinedKey
+ }
+
+ switch node := v.(type) {
+ // node has to be of those two types given how keys are sorted above
+ case *Tree:
+ tv, ok := t.values[k].(*Tree)
+ if !ok {
+ return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
+ }
+ if tv.comment != "" {
+ comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
+ start := "# "
+ if strings.HasPrefix(comment, "#") {
+ start = ""
+ }
+ writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
+ bytesCount += int64(writtenBytesCountComment)
+ if errc != nil {
+ return bytesCount, errc
+ }
+ }
+
+ var commented string
+ if parentCommented || t.commented || tv.commented {
+ commented = "# "
+ }
+ writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
+ bytesCount += int64(writtenBytesCount)
+ if err != nil {
+ return bytesCount, err
+ }
+ bytesCount, err = node.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord, parentCommented || t.commented || tv.commented)
+ if err != nil {
+ return bytesCount, err
+ }
+ case []*Tree:
+ for _, subTree := range node {
+ var commented string
+ if parentCommented || t.commented || subTree.commented {
+ commented = "# "
+ }
+ writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
+ bytesCount += int64(writtenBytesCount)
+ if err != nil {
+ return bytesCount, err
+ }
+
+ bytesCount, err = subTree.writeToOrdered(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine, ord, parentCommented || t.commented || subTree.commented)
+ if err != nil {
+ return bytesCount, err
+ }
+ }
+ }
+ default: // Simple
+ k := node.key
+ v, ok := t.values[k].(*tomlValue)
if !ok {
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
}
- if tv.comment != "" {
- comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
+
+ var commented string
+ if parentCommented || t.commented || v.commented {
+ commented = "# "
+ }
+ repr, err := tomlValueStringRepresentation(v, commented, 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)
+ writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment, "\n")
bytesCount += int64(writtenBytesCountComment)
if errc != nil {
return bytesCount, errc
}
}
- writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
+
+ quotedKey := quoteKeyIfNeeded(k)
+ writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n")
bytesCount += int64(writtenBytesCount)
if err != nil {
return bytesCount, err
}
- bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine)
- 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.writeTo(w, indent+" ", combinedKey, bytesCount, arraysOneElementPerLine)
- if err != nil {
- return bytesCount, err
- }
- }
}
}
return bytesCount, nil
}
+// quote a key if it does not fit the bare key format (A-Za-z0-9_-)
+// quoted keys use the same rules as strings
+func quoteKeyIfNeeded(k string) string {
+ // when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain
+ // keys that have already been quoted.
+ // not an ideal situation, but good enough of a stop gap.
+ if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' {
+ return k
+ }
+ isBare := true
+ for _, r := range k {
+ if !isValidBareChar(r) {
+ isBare = false
+ break
+ }
+ }
+ if isBare {
+ return k
+ }
+ return quoteKey(k)
+}
+
+func quoteKey(k string) string {
+ return "\"" + encodeTomlString(k) + "\""
+}
+
func writeStrings(w io.Writer, s ...string) (int, error) {
var n int
for i := range s {
@@ -286,12 +435,11 @@ func (t *Tree) WriteTo(w io.Writer) (int64, error) {
// 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)
+ b, err := t.Marshal()
if err != nil {
return "", err
}
- return buf.String(), nil
+ return string(b), nil
}
// String generates a human-readable representation of the current tree.