Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fe62057ea | |||
| 5f42261979 | |||
| 75654e60b8 | |||
| 091e2dc498 | |||
| 095a905e04 | |||
| ec312409d3 | |||
| 26fd12ff54 | |||
| b40204d36a | |||
| 4d5afd743f | |||
| 3ded2e09ee | |||
| 781fbae71e | |||
| 68063a447e | |||
| 84da2c4a25 | |||
| dba45d427f |
@@ -1,170 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
executors:
|
|
||||||
golang:
|
|
||||||
parameters:
|
|
||||||
version:
|
|
||||||
type: string
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:<< parameters.version >>
|
|
||||||
|
|
||||||
commands:
|
|
||||||
get_deps:
|
|
||||||
description: "Get go dependencies"
|
|
||||||
steps:
|
|
||||||
- run: go get github.com/jstemmer/go-junit-report
|
|
||||||
|
|
||||||
run_test:
|
|
||||||
description: "Run unit tests for a go module"
|
|
||||||
parameters:
|
|
||||||
test_name:
|
|
||||||
type: string
|
|
||||||
module:
|
|
||||||
type: string
|
|
||||||
coverage:
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
allow_fail:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
steps:
|
|
||||||
- run:
|
|
||||||
name: "Run tests for <<parameters.test_name>>"
|
|
||||||
command: |
|
|
||||||
TEST_DIR="/tmp/test-results/<<parameters.test_name>>"
|
|
||||||
mkdir -p ${TEST_DIR}
|
|
||||||
trap "go-junit-report </tmp/test-results/go-test.out > ${TEST_DIR}/go-test-report.xml" EXIT
|
|
||||||
go test <<parameters.module>> -race -v \
|
|
||||||
<<# parameters.coverage >>-coverprofile=/tmp/workspace/coverage.txt -covermode=atomic<</ parameters.coverage >> \
|
|
||||||
| tee /tmp/test-results/go-test.out <<# parameters.allow_fail >>|| true<</ parameters.allow_fail >>
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
go:
|
|
||||||
parameters:
|
|
||||||
version:
|
|
||||||
type: string
|
|
||||||
allow_fail:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
executor:
|
|
||||||
name: golang
|
|
||||||
version: "<<parameters.version>>"
|
|
||||||
working_directory: /go/src/github.com/pelletier/go-toml
|
|
||||||
environment:
|
|
||||||
GO111MODULE: "on"
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: mkdir -p /tmp/workspace
|
|
||||||
- run: go fmt ./... <<# parameters.allow_fail >>|| true<</ parameters.allow_fail >>
|
|
||||||
- get_deps
|
|
||||||
- run_test:
|
|
||||||
test_name: "go-toml"
|
|
||||||
module: "github.com/pelletier/go-toml"
|
|
||||||
coverage: true
|
|
||||||
allow_fail: <<parameters.allow_fail>>
|
|
||||||
- run_test:
|
|
||||||
test_name: "tomljson"
|
|
||||||
module: "github.com/pelletier/go-toml/cmd/tomljson"
|
|
||||||
allow_fail: <<parameters.allow_fail>>
|
|
||||||
- run_test:
|
|
||||||
test_name: "tomll"
|
|
||||||
module: "github.com/pelletier/go-toml/cmd/tomll"
|
|
||||||
allow_fail: <<parameters.allow_fail>>
|
|
||||||
- run_test:
|
|
||||||
test_name: "query"
|
|
||||||
module: "github.com/pelletier/go-toml/query"
|
|
||||||
allow_fail: <<parameters.allow_fail>>
|
|
||||||
- store_test_results:
|
|
||||||
path: /tmp/test-results
|
|
||||||
codecov:
|
|
||||||
docker:
|
|
||||||
- image: "circleci/golang:1.12"
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: /tmp/workspace
|
|
||||||
- run:
|
|
||||||
name: "upload to codecov"
|
|
||||||
working_directory: /tmp/workspace
|
|
||||||
command: |
|
|
||||||
curl https://codecov.io/bash > codecov.sh
|
|
||||||
bash codecov.sh -v
|
|
||||||
docker:
|
|
||||||
docker:
|
|
||||||
- image: "circleci/golang:1.12"
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- setup_remote_docker:
|
|
||||||
docker_layer_caching: true
|
|
||||||
- run: docker build -t pelletier/go-toml:$CIRCLE_SHA1 .
|
|
||||||
- run:
|
|
||||||
name: "Publish docker image"
|
|
||||||
command: |
|
|
||||||
if [ "${CIRCLE_PR_REPONAME}" == "" ]; then
|
|
||||||
IMAGE_NAME="pelletier/go-toml"
|
|
||||||
IMAGE_SHA_TAG="${IMAGE_NAME}:$CIRCLE_SHA1"
|
|
||||||
if [ "${CIRCLE_BRANCH}" = "master" ]; then
|
|
||||||
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
|
||||||
docker tag ${IMAGE_SHA_TAG} ${IMAGE_NAME}:latest
|
|
||||||
docker push ${IMAGE_NAME}:latest
|
|
||||||
fi
|
|
||||||
if [ "${CIRCLE_TAG}" != "" ]; then
|
|
||||||
docker login -u $DOCKER_USER -p $DOCKER_PASS
|
|
||||||
docker tag ${IMAGE_SHA_TAG} ${IMAGE_NAME}:${CIRCLE_TAG}
|
|
||||||
docker push ${IMAGE_NAME}:${CIRCLE_TAG}
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "not pushing docker image for forked repo"
|
|
||||||
fi
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
version: 2.1
|
|
||||||
build:
|
|
||||||
jobs:
|
|
||||||
- go:
|
|
||||||
name: "go1_11"
|
|
||||||
version: "1.11"
|
|
||||||
- go:
|
|
||||||
name: "go1_12"
|
|
||||||
version: "1.12"
|
|
||||||
post-steps:
|
|
||||||
- run: go tool cover -html=/tmp/workspace/coverage.txt -o coverage.html
|
|
||||||
- store_artifacts:
|
|
||||||
path: /tmp/workspace/coverage.txt
|
|
||||||
- store_artifacts:
|
|
||||||
path: coverage.html
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: /tmp/workspace
|
|
||||||
paths:
|
|
||||||
- coverage.txt
|
|
||||||
- go:
|
|
||||||
name: "gotip"
|
|
||||||
version: "1.12" # use as base
|
|
||||||
allow_fail: true
|
|
||||||
pre-steps:
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- go-tip-source
|
|
||||||
- run:
|
|
||||||
name: "Compile go tip"
|
|
||||||
command: |
|
|
||||||
if [ ! -d "/tmp/go" ]; then
|
|
||||||
git clone https://go.googlesource.com/go /tmp/go
|
|
||||||
fi
|
|
||||||
cd /tmp/go
|
|
||||||
git checkout master
|
|
||||||
git pull
|
|
||||||
cd src
|
|
||||||
./make.bash
|
|
||||||
echo 'export PATH="/tmp/go/bin:$PATH"' >> $BASH_ENV
|
|
||||||
- run: go version
|
|
||||||
- save_cache:
|
|
||||||
key: go-tip-source
|
|
||||||
paths:
|
|
||||||
- "/tmp/go"
|
|
||||||
- codecov:
|
|
||||||
requires:
|
|
||||||
- go1_11
|
|
||||||
- go1_12
|
|
||||||
- docker:
|
|
||||||
requires:
|
|
||||||
- codecov
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- tip
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
fast_finish: true
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
script:
|
|
||||||
- if [ -n "$(go fmt ./...)" ]; then exit 1; fi
|
|
||||||
- go test github.com/pelletier/go-toml -race -coverprofile=coverage.txt -covermode=atomic
|
|
||||||
- go test github.com/pelletier/go-toml/cmd/tomljson
|
|
||||||
- go test github.com/pelletier/go-toml/cmd/tomll
|
|
||||||
- go test github.com/pelletier/go-toml/query
|
|
||||||
- ./benchmark.sh $TRAVIS_BRANCH https://github.com/$TRAVIS_REPO_SLUG.git
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
@@ -8,3 +8,4 @@ RUN go install ./...
|
|||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=builder /go/bin/tomll /usr/bin/tomll
|
COPY --from=builder /go/bin/tomll /usr/bin/tomll
|
||||||
COPY --from=builder /go/bin/tomljson /usr/bin/tomljson
|
COPY --from=builder /go/bin/tomljson /usr/bin/tomljson
|
||||||
|
COPY --from=builder /go/bin/jsontoml /usr/bin/jsontoml
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ This library supports TOML version
|
|||||||
|
|
||||||
[](http://godoc.org/github.com/pelletier/go-toml)
|
[](http://godoc.org/github.com/pelletier/go-toml)
|
||||||
[](https://github.com/pelletier/go-toml/blob/master/LICENSE)
|
[](https://github.com/pelletier/go-toml/blob/master/LICENSE)
|
||||||
[](https://travis-ci.org/pelletier/go-toml)
|
[](https://dev.azure.com/pelletierthomas/go-toml-ci/_build/latest?definitionId=1&branchName=master)
|
||||||
[](https://ci.appveyor.com/project/pelletier/go-toml/branch/master)
|
|
||||||
[](https://codecov.io/gh/pelletier/go-toml)
|
[](https://codecov.io/gh/pelletier/go-toml)
|
||||||
[](https://goreportcard.com/report/github.com/pelletier/go-toml)
|
[](https://goreportcard.com/report/github.com/pelletier/go-toml)
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield)
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fpelletier%2Fgo-toml?ref=badge_shield)
|
||||||
@@ -101,6 +100,13 @@ Go-toml provides two handy command line tools:
|
|||||||
tomljson --help
|
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
|
### Docker image
|
||||||
|
|
||||||
Those tools are also availble as a Docker image from
|
Those tools are also availble as a Docker image from
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
version: "{build}"
|
|
||||||
|
|
||||||
# Source Config
|
|
||||||
clone_folder: c:\gopath\src\github.com\pelletier\go-toml
|
|
||||||
|
|
||||||
# Build host
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
DEPTESTBYPASS501: 1
|
|
||||||
GOVERSION: 1.12
|
|
||||||
GO111MODULE: on
|
|
||||||
|
|
||||||
init:
|
|
||||||
- git config --global core.autocrlf input
|
|
||||||
|
|
||||||
# Build
|
|
||||||
install:
|
|
||||||
# Install the specific Go version.
|
|
||||||
- rmdir c:\go /s /q
|
|
||||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi
|
|
||||||
- msiexec /i go%GOVERSION%.windows-amd64.msi /q
|
|
||||||
- choco install bzr
|
|
||||||
- set Path=c:\go\bin;c:\gopath\bin;C:\Program Files (x86)\Bazaar\;C:\Program Files\Mercurial\%Path%
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
|
|
||||||
build: false
|
|
||||||
deploy: false
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- go test github.com/pelletier/go-toml
|
|
||||||
- go test github.com/pelletier/go-toml/cmd/tomljson
|
|
||||||
- go test github.com/pelletier/go-toml/cmd/tomll
|
|
||||||
- go test github.com/pelletier/go-toml/query
|
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
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.13"
|
||||||
|
inputs:
|
||||||
|
version: "1.13"
|
||||||
|
- 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.13"
|
||||||
|
inputs:
|
||||||
|
version: "1.13"
|
||||||
|
- 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.13"
|
||||||
|
inputs:
|
||||||
|
version: "1.13"
|
||||||
|
- 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)'
|
||||||
|
- job: benchmark
|
||||||
|
displayName: "benchmark"
|
||||||
|
pool:
|
||||||
|
vmImage: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- task: GoTool@0
|
||||||
|
displayName: "Install Go 1.13"
|
||||||
|
inputs:
|
||||||
|
version: "1.13"
|
||||||
|
- 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.13"
|
||||||
|
inputs:
|
||||||
|
version: "1.13"
|
||||||
|
- 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.13:
|
||||||
|
goVersion: '1.13'
|
||||||
|
imageName: 'ubuntu-latest'
|
||||||
|
mac 1.13:
|
||||||
|
goVersion: '1.13'
|
||||||
|
imageName: 'macos-10.13'
|
||||||
|
windows 1.13:
|
||||||
|
goVersion: '1.13'
|
||||||
|
imageName: 'vs2017-win2016'
|
||||||
|
linux 1.12:
|
||||||
|
goVersion: '1.12'
|
||||||
|
imageName: 'ubuntu-latest'
|
||||||
|
mac 1.12:
|
||||||
|
goVersion: '1.12'
|
||||||
|
imageName: 'macos-10.13'
|
||||||
|
windows 1.12:
|
||||||
|
goVersion: '1.12'
|
||||||
|
imageName: 'vs2017-win2016'
|
||||||
|
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_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'
|
||||||
+1
-2
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
set -ex
|
||||||
|
|
||||||
reference_ref=${1:-master}
|
reference_ref=${1:-master}
|
||||||
reference_git=${2:-.}
|
reference_git=${2:-.}
|
||||||
@@ -8,7 +8,6 @@ reference_git=${2:-.}
|
|||||||
if ! `hash benchstat 2>/dev/null`; then
|
if ! `hash benchstat 2>/dev/null`; then
|
||||||
echo "Installing benchstat"
|
echo "Installing benchstat"
|
||||||
go get golang.org/x/perf/cmd/benchstat
|
go get golang.org/x/perf/cmd/benchstat
|
||||||
go install golang.org/x/perf/cmd/benchstat
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX`
|
tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX`
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// Jsontoml reads JSON and converts to TOML.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// cat file.toml | jsontoml > file.json
|
||||||
|
// jsontoml file1.toml > file.json
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pelletier/go-toml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintln(os.Stderr, "jsontoml can be used in two ways:")
|
||||||
|
fmt.Fprintln(os.Stderr, "Writing to STDIN and reading from STDOUT:")
|
||||||
|
fmt.Fprintln(os.Stderr, "")
|
||||||
|
fmt.Fprintln(os.Stderr, "")
|
||||||
|
fmt.Fprintln(os.Stderr, "Reading from a file name:")
|
||||||
|
fmt.Fprintln(os.Stderr, " tomljson file.toml")
|
||||||
|
}
|
||||||
|
flag.Parse()
|
||||||
|
os.Exit(processMain(flag.Args(), os.Stdin, os.Stdout, os.Stderr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func processMain(files []string, defaultInput io.Reader, output io.Writer, errorOutput io.Writer) int {
|
||||||
|
// read from stdin and print to stdout
|
||||||
|
inputReader := defaultInput
|
||||||
|
|
||||||
|
if len(files) > 0 {
|
||||||
|
file, err := os.Open(files[0])
|
||||||
|
if err != nil {
|
||||||
|
printError(err, errorOutput)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
inputReader = file
|
||||||
|
defer file.Close()
|
||||||
|
}
|
||||||
|
s, err := reader(inputReader)
|
||||||
|
if err != nil {
|
||||||
|
printError(err, errorOutput)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
io.WriteString(output, s)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func printError(err error, output io.Writer) {
|
||||||
|
io.WriteString(output, err.Error()+"\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func reader(r io.Reader) (string, error) {
|
||||||
|
jsonMap := make(map[string]interface{})
|
||||||
|
jsonBytes, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(jsonBytes, &jsonMap)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, err := toml.TreeFromMap(jsonMap)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return mapToTOML(tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToTOML(t *toml.Tree) (string, error) {
|
||||||
|
tomlBytes, err := t.ToTomlString()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(tomlBytes[:]), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func expectBufferEquality(t *testing.T, name string, buffer *bytes.Buffer, expected string) {
|
||||||
|
output := buffer.String()
|
||||||
|
if output != expected {
|
||||||
|
t.Errorf("incorrect %s: \n%sexpected %s: \n%s", name, output, name, expected)
|
||||||
|
t.Log([]rune(output))
|
||||||
|
t.Log([]rune(expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectProcessMainResults(t *testing.T, input string, args []string, exitCode int, expectedOutput string, expectedError string) {
|
||||||
|
inputReader := strings.NewReader(input)
|
||||||
|
|
||||||
|
outputBuffer := new(bytes.Buffer)
|
||||||
|
errorBuffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
returnCode := processMain(args, inputReader, outputBuffer, errorBuffer)
|
||||||
|
|
||||||
|
expectBufferEquality(t, "output", outputBuffer, expectedOutput)
|
||||||
|
expectBufferEquality(t, "error", errorBuffer, expectedError)
|
||||||
|
|
||||||
|
if returnCode != exitCode {
|
||||||
|
t.Error("incorrect return code:", returnCode, "expected", exitCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessMainReadFromStdin(t *testing.T) {
|
||||||
|
expectedOutput := `
|
||||||
|
[mytoml]
|
||||||
|
a = 42.0
|
||||||
|
`
|
||||||
|
input := `{
|
||||||
|
"mytoml": {
|
||||||
|
"a": 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
expectedError := ``
|
||||||
|
expectedExitCode := 0
|
||||||
|
|
||||||
|
expectProcessMainResults(t, input, []string{}, expectedExitCode, expectedOutput, expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessMainReadFromFile(t *testing.T) {
|
||||||
|
input := `{
|
||||||
|
"mytoml": {
|
||||||
|
"a": 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
tmpfile, err := ioutil.TempFile("", "example.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := tmpfile.Write([]byte(input)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.Remove(tmpfile.Name())
|
||||||
|
|
||||||
|
expectedOutput := `
|
||||||
|
[mytoml]
|
||||||
|
a = 42.0
|
||||||
|
`
|
||||||
|
expectedError := ``
|
||||||
|
expectedExitCode := 0
|
||||||
|
|
||||||
|
expectProcessMainResults(t, ``, []string{tmpfile.Name()}, expectedExitCode, expectedOutput, expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessMainReadFromMissingFile(t *testing.T) {
|
||||||
|
var expectedError string
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedError = `open /this/file/does/not/exist: The system cannot find the path specified.
|
||||||
|
`
|
||||||
|
} else {
|
||||||
|
expectedError = `open /this/file/does/not/exist: no such file or directory
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
expectProcessMainResults(t, ``, []string{"/this/file/does/not/exist"}, -1, ``, expectedError)
|
||||||
|
}
|
||||||
@@ -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}
|
||||||
@@ -5,5 +5,5 @@ go 1.12
|
|||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.3.1
|
github.com/BurntSushi/toml v0.3.1
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
gopkg.in/yaml.v2 v2.2.4
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,3 +5,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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=
|
||||||
|
|||||||
@@ -733,7 +733,7 @@ func (l *tomlLexer) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
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})`)
|
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
|
// Entry point
|
||||||
|
|||||||
@@ -299,6 +299,9 @@ func TestDateRegexp(t *testing.T) {
|
|||||||
if dateRegexp.FindString("1979-05-27T00:32:00.999999-07:00") == "" {
|
if dateRegexp.FindString("1979-05-27T00:32:00.999999-07:00") == "" {
|
||||||
t.Error("nano precision lexing")
|
t.Error("nano precision lexing")
|
||||||
}
|
}
|
||||||
|
if dateRegexp.FindString("1979-05-27 07:32:00Z") == "" {
|
||||||
|
t.Error("space delimiter lexing")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyEqualDate(t *testing.T) {
|
func TestKeyEqualDate(t *testing.T) {
|
||||||
@@ -320,6 +323,12 @@ func TestKeyEqualDate(t *testing.T) {
|
|||||||
{Position{1, 7}, tokenDate, "1979-05-27T00:32:00.999999-07:00"},
|
{Position{1, 7}, tokenDate, "1979-05-27T00:32:00.999999-07:00"},
|
||||||
{Position{1, 39}, tokenEOF, ""},
|
{Position{1, 39}, tokenEOF, ""},
|
||||||
})
|
})
|
||||||
|
testFlow(t, "foo = 1979-05-27 07:32:00Z", []token{
|
||||||
|
{Position{1, 1}, tokenKey, "foo"},
|
||||||
|
{Position{1, 5}, tokenEqual, "="},
|
||||||
|
{Position{1, 7}, tokenDate, "1979-05-27 07:32:00Z"},
|
||||||
|
{Position{1, 27}, tokenEOF, ""},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatEndingWithDot(t *testing.T) {
|
func TestFloatEndingWithDot(t *testing.T) {
|
||||||
|
|||||||
+50
-16
@@ -445,8 +445,11 @@ func (t *Tree) Unmarshal(v interface{}) error {
|
|||||||
// See Marshal() documentation for types mapping table.
|
// See Marshal() documentation for types mapping table.
|
||||||
func (t *Tree) Marshal() ([]byte, error) {
|
func (t *Tree) Marshal() ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := NewEncoder(&buf).Encode(t)
|
_, err := t.WriteTo(&buf)
|
||||||
return buf.Bytes(), err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parses the TOML-encoded data and stores the result in the value
|
// Unmarshal parses the TOML-encoded data and stores the result in the value
|
||||||
@@ -526,7 +529,9 @@ func (d *Decoder) unmarshal(v interface{}) error {
|
|||||||
return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
|
return errors.New("only a pointer to struct or map can be unmarshaled from TOML")
|
||||||
}
|
}
|
||||||
|
|
||||||
sval, err := d.valueFromTree(elem, d.tval)
|
vv := reflect.ValueOf(v).Elem()
|
||||||
|
|
||||||
|
sval, err := d.valueFromTree(elem, d.tval, &vv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -534,15 +539,21 @@ func (d *Decoder) unmarshal(v interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert toml tree to marshal struct or map, using marshal type
|
// Convert toml tree to marshal struct or map, using marshal type. When mval1
|
||||||
func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
// 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 {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return d.unwrapPointer(mtype, tval)
|
return d.unwrapPointer(mtype, tval, mval1)
|
||||||
}
|
}
|
||||||
var mval reflect.Value
|
var mval reflect.Value
|
||||||
switch mtype.Kind() {
|
switch mtype.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
|
if mval1 != nil {
|
||||||
|
mval = *mval1
|
||||||
|
} else {
|
||||||
mval = reflect.New(mtype).Elem()
|
mval = reflect.New(mtype).Elem()
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < mtype.NumField(); i++ {
|
for i := 0; i < mtype.NumField(); i++ {
|
||||||
mtypef := mtype.Field(i)
|
mtypef := mtype.Field(i)
|
||||||
an := annotation{tag: d.tagName}
|
an := annotation{tag: d.tagName}
|
||||||
@@ -563,7 +574,8 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val := tval.Get(key)
|
val := tval.Get(key)
|
||||||
mvalf, err := d.valueFromToml(mtypef.Type, val)
|
fval := mval.Field(i)
|
||||||
|
mvalf, err := d.valueFromToml(mtypef.Type, val, &fval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mval, formatError(err, tval.GetPosition(key))
|
return mval, formatError(err, tval.GetPosition(key))
|
||||||
}
|
}
|
||||||
@@ -604,6 +616,15 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
|
|||||||
}
|
}
|
||||||
mval.Field(i).Set(reflect.ValueOf(val))
|
mval.Field(i).Set(reflect.ValueOf(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save the old behavior above and try to check anonymous structs
|
||||||
|
if !found && opts.defaultValue == "" && mtypef.Anonymous && mtypef.Type.Kind() == reflect.Struct {
|
||||||
|
v, err := d.valueFromTree(mtypef.Type, tval, nil)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
mval.Field(i).Set(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
@@ -611,7 +632,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
|
|||||||
for _, key := range tval.Keys() {
|
for _, key := range tval.Keys() {
|
||||||
// TODO: path splits key
|
// TODO: path splits key
|
||||||
val := tval.GetPath([]string{key})
|
val := tval.GetPath([]string{key})
|
||||||
mvalf, err := d.valueFromToml(mtype.Elem(), val)
|
mvalf, err := d.valueFromToml(mtype.Elem(), val, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mval, formatError(err, tval.GetPosition(key))
|
return mval, formatError(err, tval.GetPosition(key))
|
||||||
}
|
}
|
||||||
@@ -625,7 +646,7 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value,
|
|||||||
func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
|
func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
|
||||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||||
for i := 0; i < len(tval); i++ {
|
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 {
|
if err != nil {
|
||||||
return mval, err
|
return mval, err
|
||||||
}
|
}
|
||||||
@@ -638,7 +659,7 @@ func (d *Decoder) valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.
|
|||||||
func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
||||||
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||||
for i := 0; i < len(tval); i++ {
|
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 {
|
if err != nil {
|
||||||
return mval, err
|
return mval, err
|
||||||
}
|
}
|
||||||
@@ -647,16 +668,22 @@ func (d *Decoder) valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (r
|
|||||||
return mval, nil
|
return mval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert toml value to marshal value, using marshal type
|
// Convert toml value to marshal value, using marshal type. When mval1 is non-nil
|
||||||
func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
// 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 {
|
if mtype.Kind() == reflect.Ptr {
|
||||||
return d.unwrapPointer(mtype, tval)
|
return d.unwrapPointer(mtype, tval, mval1)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := tval.(type) {
|
switch t := tval.(type) {
|
||||||
case *Tree:
|
case *Tree:
|
||||||
|
var mval11 *reflect.Value
|
||||||
|
if mtype.Kind() == reflect.Struct {
|
||||||
|
mval11 = mval1
|
||||||
|
}
|
||||||
|
|
||||||
if isTree(mtype) {
|
if isTree(mtype) {
|
||||||
return d.valueFromTree(mtype, t)
|
return d.valueFromTree(mtype, t, mval11)
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to a tree", tval, tval)
|
||||||
case []*Tree:
|
case []*Tree:
|
||||||
@@ -734,8 +761,15 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}) (reflect.V
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
func (d *Decoder) unwrapPointer(mtype reflect.Type, tval interface{}, mval1 *reflect.Value) (reflect.Value, error) {
|
||||||
val, err := d.valueFromToml(mtype.Elem(), tval)
|
var melem *reflect.Value
|
||||||
|
|
||||||
|
if mval1 != nil && !mval1.IsNil() && mtype.Elem().Kind() == reflect.Struct {
|
||||||
|
elem := mval1.Elem()
|
||||||
|
melem = &elem
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := d.valueFromToml(mtype.Elem(), tval, melem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return reflect.ValueOf(nil), err
|
return reflect.ValueOf(nil), err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
title = "TOML Marshal Testing"
|
|
||||||
|
|
||||||
[basic_map]
|
|
||||||
one = "one"
|
|
||||||
two = "two"
|
|
||||||
|
|
||||||
[long_map]
|
|
||||||
a7 = "1"
|
|
||||||
b3 = "2"
|
|
||||||
c8 = "3"
|
|
||||||
d4 = "4"
|
|
||||||
e6 = "5"
|
|
||||||
f5 = "6"
|
|
||||||
g10 = "7"
|
|
||||||
h1 = "8"
|
|
||||||
i2 = "9"
|
|
||||||
j9 = "10"
|
|
||||||
@@ -27,6 +27,7 @@ title = "TOML Marshal Testing"
|
|||||||
uint = 5001
|
uint = 5001
|
||||||
bool = true
|
bool = true
|
||||||
float = 123.4
|
float = 123.4
|
||||||
|
float64 = 123.456782132399
|
||||||
int = 5000
|
int = 5000
|
||||||
string = "Bite me"
|
string = "Bite me"
|
||||||
date = 1979-05-27T07:32:00Z
|
date = 1979-05-27T07:32:00Z
|
||||||
|
|||||||
+304
-22
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -55,6 +56,107 @@ Ystrlist = ["Howdy","Hey There"]
|
|||||||
String2 = "Three"
|
String2 = "Three"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
var marshalTestToml = []byte(`title = "TOML Marshal Testing"
|
||||||
|
|
||||||
|
[basic]
|
||||||
|
bool = true
|
||||||
|
date = 1979-05-27T07:32:00Z
|
||||||
|
float = 123.4
|
||||||
|
float64 = 123.456782132399
|
||||||
|
int = 5000
|
||||||
|
string = "Bite me"
|
||||||
|
uint = 5001
|
||||||
|
|
||||||
|
[basic_lists]
|
||||||
|
bools = [true,false,true]
|
||||||
|
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
||||||
|
floats = [12.3,45.6,78.9]
|
||||||
|
ints = [8001,8001,8002]
|
||||||
|
strings = ["One","Two","Three"]
|
||||||
|
uints = [5002,5003]
|
||||||
|
|
||||||
|
[basic_map]
|
||||||
|
one = "one"
|
||||||
|
two = "two"
|
||||||
|
|
||||||
|
[subdoc]
|
||||||
|
|
||||||
|
[subdoc.first]
|
||||||
|
name = "First"
|
||||||
|
|
||||||
|
[subdoc.second]
|
||||||
|
name = "Second"
|
||||||
|
|
||||||
|
[[subdoclist]]
|
||||||
|
name = "List.First"
|
||||||
|
|
||||||
|
[[subdoclist]]
|
||||||
|
name = "List.Second"
|
||||||
|
|
||||||
|
[[subdocptrs]]
|
||||||
|
name = "Second"
|
||||||
|
`)
|
||||||
|
|
||||||
|
var marshalOrderPreserveToml = []byte(`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"
|
||||||
|
`)
|
||||||
|
|
||||||
|
var mashalOrderPreserveMapToml = []byte(`title = "TOML Marshal Testing"
|
||||||
|
|
||||||
|
[basic_map]
|
||||||
|
one = "one"
|
||||||
|
two = "two"
|
||||||
|
|
||||||
|
[long_map]
|
||||||
|
a7 = "1"
|
||||||
|
b3 = "2"
|
||||||
|
c8 = "3"
|
||||||
|
d4 = "4"
|
||||||
|
e6 = "5"
|
||||||
|
f5 = "6"
|
||||||
|
g10 = "7"
|
||||||
|
h1 = "8"
|
||||||
|
i2 = "9"
|
||||||
|
j9 = "10"
|
||||||
|
`)
|
||||||
|
|
||||||
func TestBasicMarshal(t *testing.T) {
|
func TestBasicMarshal(t *testing.T) {
|
||||||
result, err := Marshal(basicTestData)
|
result, err := Marshal(basicTestData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -135,7 +237,8 @@ type testMapDoc struct {
|
|||||||
type testDocBasics struct {
|
type testDocBasics struct {
|
||||||
Uint uint `toml:"uint"`
|
Uint uint `toml:"uint"`
|
||||||
Bool bool `toml:"bool"`
|
Bool bool `toml:"bool"`
|
||||||
Float float32 `toml:"float"`
|
Float32 float32 `toml:"float"`
|
||||||
|
Float64 float64 `toml:"float64"`
|
||||||
Int int `toml:"int"`
|
Int int `toml:"int"`
|
||||||
String *string `toml:"string"`
|
String *string `toml:"string"`
|
||||||
Date time.Time `toml:"date"`
|
Date time.Time `toml:"date"`
|
||||||
@@ -174,7 +277,8 @@ var docData = testDoc{
|
|||||||
Basics: testDocBasics{
|
Basics: testDocBasics{
|
||||||
Bool: true,
|
Bool: true,
|
||||||
Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
|
||||||
Float: 123.4,
|
Float32: 123.4,
|
||||||
|
Float64: 123.456782132399,
|
||||||
Int: 5000,
|
Int: 5000,
|
||||||
Uint: 5001,
|
Uint: 5001,
|
||||||
String: &biteMe,
|
String: &biteMe,
|
||||||
@@ -231,9 +335,8 @@ func TestDocMarshal(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expected, _ := ioutil.ReadFile("marshal_test.toml")
|
if !bytes.Equal(result, marshalTestToml) {
|
||||||
if !bytes.Equal(result, expected) {
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
|
||||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,9 +346,8 @@ func TestDocMarshalOrdered(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expected, _ := ioutil.ReadFile("marshal_OrderPreserve_test.toml")
|
if !bytes.Equal(result.Bytes(), marshalOrderPreserveToml) {
|
||||||
if !bytes.Equal(result.Bytes(), expected) {
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalOrderPreserveToml, result.Bytes())
|
||||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,9 +356,8 @@ func TestDocMarshalMaps(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expected, _ := ioutil.ReadFile("marshal_OrderPreserve_Map_test.toml")
|
if !bytes.Equal(result, mashalOrderPreserveMapToml) {
|
||||||
if !bytes.Equal(result, expected) {
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result)
|
||||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,9 +367,8 @@ func TestDocMarshalOrderedMaps(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expected, _ := ioutil.ReadFile("marshal_OrderPreserve_Map_test.toml")
|
if !bytes.Equal(result.Bytes(), mashalOrderPreserveMapToml) {
|
||||||
if !bytes.Equal(result.Bytes(), expected) {
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result.Bytes())
|
||||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,16 +377,15 @@ func TestDocMarshalPointer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
expected, _ := ioutil.ReadFile("marshal_test.toml")
|
|
||||||
if !bytes.Equal(result, expected) {
|
if !bytes.Equal(result, marshalTestToml) {
|
||||||
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocUnmarshal(t *testing.T) {
|
func TestDocUnmarshal(t *testing.T) {
|
||||||
result := testDoc{}
|
result := testDoc{}
|
||||||
tomlData, _ := ioutil.ReadFile("marshal_test.toml")
|
err := Unmarshal(marshalTestToml, &result)
|
||||||
err := Unmarshal(tomlData, &result)
|
|
||||||
expected := docData
|
expected := docData
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -299,11 +398,22 @@ func TestDocUnmarshal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocPartialUnmarshal(t *testing.T) {
|
func TestDocPartialUnmarshal(t *testing.T) {
|
||||||
result := testDocSubs{}
|
file, err := ioutil.TempFile("", "test-*.toml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
tree, _ := LoadFile("marshal_test.toml")
|
err = ioutil.WriteFile(file.Name(), marshalTestToml, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree, _ := LoadFile(file.Name())
|
||||||
subTree := tree.Get("subdoc").(*Tree)
|
subTree := tree.Get("subdoc").(*Tree)
|
||||||
err := subTree.Unmarshal(&result)
|
|
||||||
|
result := testDocSubs{}
|
||||||
|
err = subTree.Unmarshal(&result)
|
||||||
expected := docData.Subdocs
|
expected := docData.Subdocs
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -1417,3 +1527,175 @@ func TestUnmarshalDefaultFailureUnsupported(t *testing.T) {
|
|||||||
t.Fatal("should error")
|
t.Fatal("should error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalNestedAnonymousStructs(t *testing.T) {
|
||||||
|
type Nested struct {
|
||||||
|
Value string `toml:"nested_field"`
|
||||||
|
}
|
||||||
|
type Deep struct {
|
||||||
|
Nested
|
||||||
|
}
|
||||||
|
type Document struct {
|
||||||
|
Deep
|
||||||
|
Value string `toml:"own_field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc Document
|
||||||
|
|
||||||
|
err := Unmarshal([]byte(`nested_field = "nested value"`+"\n"+`own_field = "own value"`), &doc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("should not error")
|
||||||
|
}
|
||||||
|
if doc.Value != "own value" || doc.Nested.Value != "nested value" {
|
||||||
|
t.Fatal("unexpected values")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalNestedAnonymousStructs_Controversial(t *testing.T) {
|
||||||
|
type Nested struct {
|
||||||
|
Value string `toml:"nested"`
|
||||||
|
}
|
||||||
|
type Deep struct {
|
||||||
|
Nested
|
||||||
|
}
|
||||||
|
type Document struct {
|
||||||
|
Deep
|
||||||
|
Value string `toml:"own"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc Document
|
||||||
|
|
||||||
|
err := Unmarshal([]byte(`nested = "nested value"`+"\n"+`own = "own value"`), &doc)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type unexportedFieldPreservationTest struct {
|
||||||
|
Exported string `toml:"exported"`
|
||||||
|
unexported string
|
||||||
|
Nested1 unexportedFieldPreservationTestNested `toml:"nested1"`
|
||||||
|
Nested2 *unexportedFieldPreservationTestNested `toml:"nested2"`
|
||||||
|
Nested3 *unexportedFieldPreservationTestNested `toml:"nested3"`
|
||||||
|
Slice1 []unexportedFieldPreservationTestNested `toml:"slice1"`
|
||||||
|
Slice2 []*unexportedFieldPreservationTestNested `toml:"slice2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type unexportedFieldPreservationTestNested struct {
|
||||||
|
Exported1 string `toml:"exported1"`
|
||||||
|
unexported1 string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalPreservesUnexportedFields(t *testing.T) {
|
||||||
|
toml := `
|
||||||
|
exported = "visible"
|
||||||
|
unexported = "ignored"
|
||||||
|
|
||||||
|
[nested1]
|
||||||
|
exported1 = "visible1"
|
||||||
|
unexported1 = "ignored1"
|
||||||
|
|
||||||
|
[nested2]
|
||||||
|
exported1 = "visible2"
|
||||||
|
unexported1 = "ignored2"
|
||||||
|
|
||||||
|
[nested3]
|
||||||
|
exported1 = "visible3"
|
||||||
|
unexported1 = "ignored3"
|
||||||
|
|
||||||
|
[[slice1]]
|
||||||
|
exported1 = "visible3"
|
||||||
|
|
||||||
|
[[slice1]]
|
||||||
|
exported1 = "visible4"
|
||||||
|
|
||||||
|
[[slice2]]
|
||||||
|
exported1 = "visible5"
|
||||||
|
`
|
||||||
|
|
||||||
|
t.Run("unexported field should not be set from toml", func(t *testing.T) {
|
||||||
|
var actual unexportedFieldPreservationTest
|
||||||
|
err := Unmarshal([]byte(toml), &actual)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("did not expect an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
expect := unexportedFieldPreservationTest{
|
||||||
|
Exported: "visible",
|
||||||
|
unexported: "",
|
||||||
|
Nested1: unexportedFieldPreservationTestNested{"visible1", ""},
|
||||||
|
Nested2: &unexportedFieldPreservationTestNested{"visible2", ""},
|
||||||
|
Nested3: &unexportedFieldPreservationTestNested{"visible3", ""},
|
||||||
|
Slice1: []unexportedFieldPreservationTestNested{
|
||||||
|
{Exported1: "visible3"},
|
||||||
|
{Exported1: "visible4"},
|
||||||
|
},
|
||||||
|
Slice2: []*unexportedFieldPreservationTestNested{
|
||||||
|
{Exported1: "visible5"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, expect) {
|
||||||
|
t.Fatalf("%+v did not equal %+v", actual, expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unexported field should be preserved", func(t *testing.T) {
|
||||||
|
actual := unexportedFieldPreservationTest{
|
||||||
|
Exported: "foo",
|
||||||
|
unexported: "bar",
|
||||||
|
Nested1: unexportedFieldPreservationTestNested{"baz", "bax"},
|
||||||
|
Nested2: nil,
|
||||||
|
Nested3: &unexportedFieldPreservationTestNested{"baz", "bax"},
|
||||||
|
}
|
||||||
|
err := Unmarshal([]byte(toml), &actual)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("did not expect an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
expect := unexportedFieldPreservationTest{
|
||||||
|
Exported: "visible",
|
||||||
|
unexported: "bar",
|
||||||
|
Nested1: unexportedFieldPreservationTestNested{"visible1", "bax"},
|
||||||
|
Nested2: &unexportedFieldPreservationTestNested{"visible2", ""},
|
||||||
|
Nested3: &unexportedFieldPreservationTestNested{"visible3", "bax"},
|
||||||
|
Slice1: []unexportedFieldPreservationTestNested{
|
||||||
|
{Exported1: "visible3"},
|
||||||
|
{Exported1: "visible4"},
|
||||||
|
},
|
||||||
|
Slice2: []*unexportedFieldPreservationTestNested{
|
||||||
|
{Exported1: "visible5"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, expect) {
|
||||||
|
t.Fatalf("%+v did not equal %+v", actual, expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeMarshal(t *testing.T) {
|
||||||
|
cases := [][]byte{
|
||||||
|
basicTestToml,
|
||||||
|
marshalTestToml,
|
||||||
|
emptyTestToml,
|
||||||
|
pointerTestToml,
|
||||||
|
}
|
||||||
|
for _, expected := range cases {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
tree, err := LoadBytes(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result, err := tree.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, expected) {
|
||||||
|
t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ title = "TOML Marshal Testing"
|
|||||||
bool = true
|
bool = true
|
||||||
date = 1979-05-27T07:32:00Z
|
date = 1979-05-27T07:32:00Z
|
||||||
float = 123.4
|
float = 123.4
|
||||||
|
float64 = 123.456782132399
|
||||||
int = 5000
|
int = 5000
|
||||||
string = "Bite me"
|
string = "Bite me"
|
||||||
uint = 5001
|
uint = 5001
|
||||||
|
|||||||
@@ -313,7 +313,11 @@ func (p *tomlParser) parseRvalue() interface{} {
|
|||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
case tokenDate:
|
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 {
|
if err != nil {
|
||||||
p.raiseError(tok, "%s", err)
|
p.raiseError(tok, "%s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,6 +225,13 @@ func TestDateNano(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDateSpaceDelimiter(t *testing.T) {
|
||||||
|
tree, err := Load("odt4 = 1979-05-27 07:32:00Z")
|
||||||
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
|
"odt4": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestSimpleString(t *testing.T) {
|
func TestSimpleString(t *testing.T) {
|
||||||
tree, err := Load("a = \"hello world\"")
|
tree, err := Load("a = \"hello world\"")
|
||||||
assertTree(t, tree, err, map[string]interface{}{
|
assertTree(t, tree, err, map[string]interface{}{
|
||||||
|
|||||||
+44
-9
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -106,12 +107,20 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
|
|||||||
case int64:
|
case int64:
|
||||||
return strconv.FormatInt(value, 10), nil
|
return strconv.FormatInt(value, 10), nil
|
||||||
case float64:
|
case float64:
|
||||||
// Ensure a round float does contain a decimal point. Otherwise feeding
|
// Default bit length is full 64
|
||||||
// the output back to the parser would convert to an integer.
|
bits := 64
|
||||||
if math.Trunc(value) == value {
|
// Float panics if nan is used
|
||||||
return strings.ToLower(strconv.FormatFloat(value, 'f', 1, 32)), nil
|
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
|
||||||
}
|
}
|
||||||
return strings.ToLower(strconv.FormatFloat(value, 'f', -1, 32)), nil
|
}
|
||||||
|
if math.Trunc(value) == value {
|
||||||
|
return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil
|
||||||
|
}
|
||||||
|
return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil
|
||||||
case string:
|
case string:
|
||||||
if tv.multiline {
|
if tv.multiline {
|
||||||
return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
|
return "\"\"\"\n" + encodeMultilineTomlString(value) + "\"\"\"", nil
|
||||||
@@ -354,7 +363,8 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i
|
|||||||
if v.commented {
|
if v.commented {
|
||||||
commented = "# "
|
commented = "# "
|
||||||
}
|
}
|
||||||
writtenBytesCount, err := writeStrings(w, indent, commented, k, " = ", repr, "\n")
|
quotedKey := quoteKeyIfNeeded(k)
|
||||||
|
writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n")
|
||||||
bytesCount += int64(writtenBytesCount)
|
bytesCount += int64(writtenBytesCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bytesCount, err
|
return bytesCount, err
|
||||||
@@ -365,6 +375,32 @@ func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount i
|
|||||||
return bytesCount, nil
|
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) {
|
func writeStrings(w io.Writer, s ...string) (int, error) {
|
||||||
var n int
|
var n int
|
||||||
for i := range s {
|
for i := range s {
|
||||||
@@ -387,12 +423,11 @@ func (t *Tree) WriteTo(w io.Writer) (int64, error) {
|
|||||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser.
|
// 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.
|
// If the conversion cannot be performed, ToString returns a non-nil error.
|
||||||
func (t *Tree) ToTomlString() (string, error) {
|
func (t *Tree) ToTomlString() (string, error) {
|
||||||
var buf bytes.Buffer
|
b, err := t.Marshal()
|
||||||
_, err := t.WriteTo(&buf)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return buf.String(), nil
|
return string(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// String generates a human-readable representation of the current tree.
|
// String generates a human-readable representation of the current tree.
|
||||||
|
|||||||
@@ -327,6 +327,30 @@ c = nan`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue290(t *testing.T) {
|
||||||
|
tomlString :=
|
||||||
|
`[table]
|
||||||
|
"127.0.0.1" = "value"
|
||||||
|
"127.0.0.1:8028" = "value"
|
||||||
|
"character encoding" = "value"
|
||||||
|
"ʎǝʞ" = "value"`
|
||||||
|
|
||||||
|
t1, err := Load(tomlString)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("load err:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := t1.ToTomlString()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ToTomlString err:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Load(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("reload err:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkTreeToTomlString(b *testing.B) {
|
func BenchmarkTreeToTomlString(b *testing.B) {
|
||||||
toml, err := Load(sampleHard)
|
toml, err := Load(sampleHard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user