[chore]: Bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#1375)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/spf13/viper/releases) - [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: github.com/spf13/viper dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
3e4dc6bff3
commit
98a09b5633
9
go.mod
9
go.mod
|
@ -38,7 +38,7 @@ require (
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/viper v1.14.0
|
github.com/spf13/viper v1.15.0
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/superseriousbusiness/activity v1.2.1-gts
|
github.com/superseriousbusiness/activity v1.2.1-gts
|
||||||
github.com/superseriousbusiness/exif-terminator v0.5.0
|
github.com/superseriousbusiness/exif-terminator v0.5.0
|
||||||
|
@ -120,14 +120,13 @@ require (
|
||||||
github.com/klauspost/compress v1.15.9 // indirect
|
github.com/klauspost/compress v1.15.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/magiconair/properties v1.8.6 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
@ -135,11 +134,11 @@ require (
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/rs/xid v1.4.0 // indirect
|
github.com/rs/xid v1.4.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
github.com/spf13/afero v1.9.2 // indirect
|
github.com/spf13/afero v1.9.3 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.4.1 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.6.4 // indirect
|
github.com/tdewolff/parse/v2 v2.6.4 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
|
|
18
go.sum
18
go.sum
|
@ -415,8 +415,8 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
|
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
|
||||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
@ -460,8 +460,6 @@ github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNia
|
||||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||||
|
@ -506,8 +504,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
|
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||||
|
@ -516,8 +514,8 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
|
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
|
||||||
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
|
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
@ -533,8 +531,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
||||||
github.com/superseriousbusiness/activity v1.2.1-gts h1:wh7v0zYa1mJmqB35PSfvgl4cs51Dh5PyfKvcZLSxMQU=
|
github.com/superseriousbusiness/activity v1.2.1-gts h1:wh7v0zYa1mJmqB35PSfvgl4cs51Dh5PyfKvcZLSxMQU=
|
||||||
github.com/superseriousbusiness/activity v1.2.1-gts/go.mod h1:AZw0Xb4Oju8rmaJCZ21gc5CPg47MmNgyac+Hx5jo8VM=
|
github.com/superseriousbusiness/activity v1.2.1-gts/go.mod h1:AZw0Xb4Oju8rmaJCZ21gc5CPg47MmNgyac+Hx5jo8VM=
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.3.x
|
|
||||||
- 1.4.x
|
|
||||||
- 1.5.x
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- "1.10.x"
|
|
||||||
- "1.11.x"
|
|
||||||
- "1.12.x"
|
|
||||||
- "1.13.x"
|
|
||||||
- "1.14.x"
|
|
||||||
- "1.15.x"
|
|
||||||
- "1.16.x"
|
|
||||||
- tip
|
|
|
@ -1,5 +1,50 @@
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### [1.8.7](https://github.com/magiconair/properties/tree/v1.8.7) - 08 Dec 2022
|
||||||
|
|
||||||
|
* [PR #65](https://github.com/magiconair/properties/pull/65): Speedup Merge
|
||||||
|
|
||||||
|
Thanks to [@AdityaVallabh](https://github.com/AdityaVallabh) for the patch.
|
||||||
|
|
||||||
|
* [PR #66](https://github.com/magiconair/properties/pull/66): use github actions
|
||||||
|
|
||||||
|
### [1.8.6](https://github.com/magiconair/properties/tree/v1.8.6) - 23 Feb 2022
|
||||||
|
|
||||||
|
* [PR #57](https://github.com/magiconair/properties/pull/57):Fix "unreachable code" lint error
|
||||||
|
|
||||||
|
Thanks to [@ellie](https://github.com/ellie) for the patch.
|
||||||
|
|
||||||
|
* [PR #63](https://github.com/magiconair/properties/pull/63): Make TestMustGetParsedDuration backwards compatible
|
||||||
|
|
||||||
|
This patch ensures that the `TestMustGetParsedDuration` still works with `go1.3` to make the
|
||||||
|
author happy until it affects real users.
|
||||||
|
|
||||||
|
Thanks to [@maage](https://github.com/maage) for the patch.
|
||||||
|
|
||||||
|
### [1.8.5](https://github.com/magiconair/properties/tree/v1.8.5) - 24 Mar 2021
|
||||||
|
|
||||||
|
* [PR #55](https://github.com/magiconair/properties/pull/55): Fix: Encoding Bug in Comments
|
||||||
|
|
||||||
|
When reading comments \ are loaded correctly, but when writing they are then
|
||||||
|
replaced by \\. This leads to wrong comments when writing and reading multiple times.
|
||||||
|
|
||||||
|
Thanks to [@doxsch](https://github.com/doxsch) for the patch.
|
||||||
|
|
||||||
|
### [1.8.4](https://github.com/magiconair/properties/tree/v1.8.4) - 23 Sep 2020
|
||||||
|
|
||||||
|
* [PR #50](https://github.com/magiconair/properties/pull/50): enhance error message for circular references
|
||||||
|
|
||||||
|
Thanks to [@sriv](https://github.com/sriv) for the patch.
|
||||||
|
|
||||||
|
### [1.8.3](https://github.com/magiconair/properties/tree/v1.8.3) - 14 Sep 2020
|
||||||
|
|
||||||
|
* [PR #49](https://github.com/magiconair/properties/pull/49): Include the key in error message causing the circular reference
|
||||||
|
|
||||||
|
The change is include the key in the error message which is causing the circular
|
||||||
|
reference when parsing/loading the properties files.
|
||||||
|
|
||||||
|
Thanks to [@haroon-sheikh](https://github.com/haroon-sheikh) for the patch.
|
||||||
|
|
||||||
### [1.8.2](https://github.com/magiconair/properties/tree/v1.8.2) - 25 Aug 2020
|
### [1.8.2](https://github.com/magiconair/properties/tree/v1.8.2) - 25 Aug 2020
|
||||||
|
|
||||||
* [PR #36](https://github.com/magiconair/properties/pull/36): Escape backslash on write
|
* [PR #36](https://github.com/magiconair/properties/pull/36): Escape backslash on write
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -152,5 +152,4 @@
|
||||||
// http://en.wikipedia.org/wiki/.properties
|
// http://en.wikipedia.org/wiki/.properties
|
||||||
//
|
//
|
||||||
// http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29
|
// http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29
|
||||||
//
|
|
||||||
package properties
|
package properties
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import "flag"
|
||||||
// the respective key for flag.Flag.Name.
|
// the respective key for flag.Flag.Name.
|
||||||
//
|
//
|
||||||
// It's use is recommended with command line arguments as in:
|
// It's use is recommended with command line arguments as in:
|
||||||
|
//
|
||||||
// flag.Parse()
|
// flag.Parse()
|
||||||
// p.MustFlag(flag.CommandLine)
|
// p.MustFlag(flag.CommandLine)
|
||||||
func (p *Properties) MustFlag(dst *flag.FlagSet) {
|
func (p *Properties) MustFlag(dst *flag.FlagSet) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -700,22 +700,17 @@ func (p *Properties) Delete(key string) {
|
||||||
|
|
||||||
// Merge merges properties, comments and keys from other *Properties into p
|
// Merge merges properties, comments and keys from other *Properties into p
|
||||||
func (p *Properties) Merge(other *Properties) {
|
func (p *Properties) Merge(other *Properties) {
|
||||||
|
for _, k := range other.k {
|
||||||
|
if _, ok := p.m[k]; !ok {
|
||||||
|
p.k = append(p.k, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
for k, v := range other.m {
|
for k, v := range other.m {
|
||||||
p.m[k] = v
|
p.m[k] = v
|
||||||
}
|
}
|
||||||
for k, v := range other.c {
|
for k, v := range other.c {
|
||||||
p.c[k] = v
|
p.c[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
outer:
|
|
||||||
for _, otherKey := range other.k {
|
|
||||||
for _, key := range p.k {
|
|
||||||
if otherKey == key {
|
|
||||||
continue outer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.k = append(p.k, otherKey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 Frank Schroeder. All rights reserved.
|
// Copyright 2013-2022 Frank Schroeder. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
cmd/tomll/tomll
|
|
||||||
cmd/tomljson/tomljson
|
|
|
@ -1,5 +0,0 @@
|
||||||
test_program/test_program_bin
|
|
||||||
fuzz/
|
|
||||||
cmd/tomll/tomll
|
|
||||||
cmd/tomljson/tomljson
|
|
||||||
cmd/tomltestgen/tomltestgen
|
|
|
@ -1,132 +0,0 @@
|
||||||
## 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 [pkg.go.dev][pkg.go.dev]. 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
|
|
||||||
[pkg.go.dev]: https://pkg.go.dev/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
|
|
|
@ -1,11 +0,0 @@
|
||||||
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
|
|
|
@ -1,247 +0,0 @@
|
||||||
The bulk of github.com/pelletier/go-toml is distributed under the MIT license
|
|
||||||
(see below), with the exception of localtime.go and localtime.test.go.
|
|
||||||
Those two files have been copied over from Google's civil library at revision
|
|
||||||
ed46f5086358513cf8c25f8e3f022cb838a49d66, and are distributed under the Apache
|
|
||||||
2.0 license (see below).
|
|
||||||
|
|
||||||
|
|
||||||
github.com/pelletier/go-toml:
|
|
||||||
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 - 2021 Thomas Pelletier, Eric Anderton
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
localtime.go, localtime_test.go:
|
|
||||||
|
|
||||||
Originals:
|
|
||||||
https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/civil/civil.go
|
|
||||||
https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/civil/civil_test.go
|
|
||||||
Changes:
|
|
||||||
* Renamed files from civil* to localtime*.
|
|
||||||
* Package changed from civil to toml.
|
|
||||||
* 'Local' prefix added to all structs.
|
|
||||||
License:
|
|
||||||
https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/LICENSE
|
|
||||||
|
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,29 +0,0 @@
|
||||||
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)
|
|
|
@ -1,5 +0,0 @@
|
||||||
**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).
|
|
|
@ -1,176 +0,0 @@
|
||||||
# go-toml
|
|
||||||
|
|
||||||
Go library for the [TOML](https://toml.io/) format.
|
|
||||||
|
|
||||||
This library supports TOML version
|
|
||||||
[v1.0.0-rc.3](https://toml.io/en/v1.0.0-rc.3)
|
|
||||||
|
|
||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/pelletier/go-toml.svg)](https://pkg.go.dev/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://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)
|
|
||||||
|
|
||||||
|
|
||||||
## Development status
|
|
||||||
|
|
||||||
**ℹ️ Consider go-toml v2!**
|
|
||||||
|
|
||||||
The next version of go-toml is in [active development][v2-dev], and
|
|
||||||
[nearing completion][v2-map].
|
|
||||||
|
|
||||||
Though technically in beta, v2 is already more tested, [fixes bugs][v1-bugs],
|
|
||||||
and [much faster][v2-bench]. If you only need reading and writing TOML documents
|
|
||||||
(majority of cases), those features are implemented and the API unlikely to
|
|
||||||
change.
|
|
||||||
|
|
||||||
The remaining features will be added shortly. While pull-requests are welcome on
|
|
||||||
v1, no active development is expected on it. When v2.0.0 is released, v1 will be
|
|
||||||
deprecated.
|
|
||||||
|
|
||||||
👉 [go-toml v2][v2]
|
|
||||||
|
|
||||||
[v2]: https://github.com/pelletier/go-toml/tree/v2
|
|
||||||
[v2-map]: https://github.com/pelletier/go-toml/discussions/506
|
|
||||||
[v2-dev]: https://github.com/pelletier/go-toml/tree/v2
|
|
||||||
[v1-bugs]: https://github.com/pelletier/go-toml/issues?q=is%3Aissue+is%3Aopen+label%3Av2-fixed
|
|
||||||
[v2-bench]: https://github.com/pelletier/go-toml/tree/v2#benchmarks
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
Go-toml provides the following features for using data parsed from TOML documents:
|
|
||||||
|
|
||||||
* Load TOML documents from files and string data
|
|
||||||
* Easily navigate TOML structure using Tree
|
|
||||||
* Marshaling and unmarshaling to and from data structures
|
|
||||||
* Line & column position data for all parsed elements
|
|
||||||
* [Query support similar to JSON-Path](query/)
|
|
||||||
* Syntax errors contain line and column numbers
|
|
||||||
|
|
||||||
## Import
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/pelletier/go-toml"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage example
|
|
||||||
|
|
||||||
Read a TOML document:
|
|
||||||
|
|
||||||
```go
|
|
||||||
config, _ := toml.Load(`
|
|
||||||
[postgres]
|
|
||||||
user = "pelletier"
|
|
||||||
password = "mypassword"`)
|
|
||||||
// retrieve data directly
|
|
||||||
user := config.Get("postgres.user").(string)
|
|
||||||
|
|
||||||
// or using an intermediate object
|
|
||||||
postgresConfig := config.Get("postgres").(*toml.Tree)
|
|
||||||
password := postgresConfig.Get("password").(string)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use Unmarshal:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Postgres struct {
|
|
||||||
User string
|
|
||||||
Password string
|
|
||||||
}
|
|
||||||
type Config struct {
|
|
||||||
Postgres Postgres
|
|
||||||
}
|
|
||||||
|
|
||||||
doc := []byte(`
|
|
||||||
[Postgres]
|
|
||||||
User = "pelletier"
|
|
||||||
Password = "mypassword"`)
|
|
||||||
|
|
||||||
config := Config{}
|
|
||||||
toml.Unmarshal(doc, &config)
|
|
||||||
fmt.Println("user=", config.Postgres.User)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use a query:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// use a query to gather elements without walking the tree
|
|
||||||
q, _ := query.Compile("$..[user,password]")
|
|
||||||
results := q.Execute(config)
|
|
||||||
for ii, item := range results.Values() {
|
|
||||||
fmt.Printf("Query result %d: %v\n", ii, item)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
The documentation and additional examples are available at
|
|
||||||
[pkg.go.dev](https://pkg.go.dev/github.com/pelletier/go-toml).
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
Go-toml provides three handy command line tools:
|
|
||||||
|
|
||||||
* `tomll`: Reads TOML files and lints them.
|
|
||||||
|
|
||||||
```
|
|
||||||
go install github.com/pelletier/go-toml/cmd/tomll
|
|
||||||
tomll --help
|
|
||||||
```
|
|
||||||
* `tomljson`: Reads a TOML file and outputs its JSON representation.
|
|
||||||
|
|
||||||
```
|
|
||||||
go install github.com/pelletier/go-toml/cmd/tomljson
|
|
||||||
tomljson --help
|
|
||||||
```
|
|
||||||
|
|
||||||
* `jsontoml`: Reads a JSON file and outputs a TOML representation.
|
|
||||||
|
|
||||||
```
|
|
||||||
go install github.com/pelletier/go-toml/cmd/jsontoml
|
|
||||||
jsontoml --help
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker image
|
|
||||||
|
|
||||||
Those tools are also available as a Docker image from
|
|
||||||
[dockerhub](https://hub.docker.com/r/pelletier/go-toml). For example, to
|
|
||||||
use `tomljson`:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker run -v $PWD:/workdir pelletier/go-toml tomljson /workdir/example.toml
|
|
||||||
```
|
|
||||||
|
|
||||||
Only master (`latest`) and tagged versions are published to dockerhub. You
|
|
||||||
can build your own image as usual:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker build -t go-toml .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contribute
|
|
||||||
|
|
||||||
Feel free to report bugs and patches using GitHub's pull requests system on
|
|
||||||
[pelletier/go-toml](https://github.com/pelletier/go-toml). Any feedback would be
|
|
||||||
much appreciated!
|
|
||||||
|
|
||||||
### Run tests
|
|
||||||
|
|
||||||
`go test ./...`
|
|
||||||
|
|
||||||
### Fuzzing
|
|
||||||
|
|
||||||
The script `./fuzz.sh` is available to
|
|
||||||
run [go-fuzz](https://github.com/dvyukov/go-fuzz) on go-toml.
|
|
||||||
|
|
||||||
## Versioning
|
|
||||||
|
|
||||||
Go-toml follows [Semantic Versioning](http://semver.org/). The supported version
|
|
||||||
of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of
|
|
||||||
this document. The last two major versions of Go are supported
|
|
||||||
(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License (MIT) + Apache 2.0. Read [LICENSE](LICENSE).
|
|
|
@ -1,19 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Use this section to tell people about which versions of your project are
|
|
||||||
currently being supported with security updates.
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| ---------- | ------------------ |
|
|
||||||
| Latest 2.x | :white_check_mark: |
|
|
||||||
| All 1.x | :x: |
|
|
||||||
| All 0.x | :x: |
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Email a vulnerability report to `security@pelletier.codes`. Make sure to include
|
|
||||||
as many details as possible to reproduce the vulnerability. This is a
|
|
||||||
side-project: I will try to get back to you as quickly as possible, time
|
|
||||||
permitting in my personal life. Providing a working patch helps very much!
|
|
|
@ -1,188 +0,0 @@
|
||||||
trigger:
|
|
||||||
- master
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- stage: run_checks
|
|
||||||
displayName: "Check"
|
|
||||||
dependsOn: []
|
|
||||||
jobs:
|
|
||||||
- job: fmt
|
|
||||||
displayName: "fmt"
|
|
||||||
pool:
|
|
||||||
vmImage: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- task: GoTool@0
|
|
||||||
displayName: "Install Go 1.16"
|
|
||||||
inputs:
|
|
||||||
version: "1.16"
|
|
||||||
- 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.16"
|
|
||||||
inputs:
|
|
||||||
version: "1.16"
|
|
||||||
- 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.16"
|
|
||||||
inputs:
|
|
||||||
version: "1.16"
|
|
||||||
- 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: go_unit_tests
|
|
||||||
displayName: "unit tests"
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
linux 1.16:
|
|
||||||
goVersion: '1.16'
|
|
||||||
imageName: 'ubuntu-latest'
|
|
||||||
mac 1.16:
|
|
||||||
goVersion: '1.16'
|
|
||||||
imageName: 'macOS-latest'
|
|
||||||
windows 1.16:
|
|
||||||
goVersion: '1.16'
|
|
||||||
imageName: 'windows-latest'
|
|
||||||
linux 1.15:
|
|
||||||
goVersion: '1.15'
|
|
||||||
imageName: 'ubuntu-latest'
|
|
||||||
mac 1.15:
|
|
||||||
goVersion: '1.15'
|
|
||||||
imageName: 'macOS-latest'
|
|
||||||
windows 1.15:
|
|
||||||
goVersion: '1.15'
|
|
||||||
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.16
|
|
||||||
- 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'
|
|
|
@ -1,35 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
reference_ref=${1:-master}
|
|
||||||
reference_git=${2:-.}
|
|
||||||
|
|
||||||
if ! `hash benchstat 2>/dev/null`; then
|
|
||||||
echo "Installing benchstat"
|
|
||||||
go get golang.org/x/perf/cmd/benchstat
|
|
||||||
fi
|
|
||||||
|
|
||||||
tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX`
|
|
||||||
ref_tempdir="${tempdir}/ref"
|
|
||||||
ref_benchmark="${ref_tempdir}/benchmark-`echo -n ${reference_ref}|tr -s '/' '-'`.txt"
|
|
||||||
local_benchmark="`pwd`/benchmark-local.txt"
|
|
||||||
|
|
||||||
echo "=== ${reference_ref} (${ref_tempdir})"
|
|
||||||
git clone ${reference_git} ${ref_tempdir} >/dev/null 2>/dev/null
|
|
||||||
pushd ${ref_tempdir} >/dev/null
|
|
||||||
git checkout ${reference_ref} >/dev/null 2>/dev/null
|
|
||||||
go test -bench=. -benchmem | tee ${ref_benchmark}
|
|
||||||
cd benchmark
|
|
||||||
go test -bench=. -benchmem | tee -a ${ref_benchmark}
|
|
||||||
popd >/dev/null
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=== local"
|
|
||||||
go test -bench=. -benchmem | tee ${local_benchmark}
|
|
||||||
cd benchmark
|
|
||||||
go test -bench=. -benchmem | tee -a ${local_benchmark}
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=== diff"
|
|
||||||
benchstat -delta-test=none ${ref_benchmark} ${local_benchmark}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Package toml is a TOML parser and manipulation library.
|
|
||||||
//
|
|
||||||
// This version supports the specification as described in
|
|
||||||
// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md
|
|
||||||
//
|
|
||||||
// Marshaling
|
|
||||||
//
|
|
||||||
// Go-toml can marshal and unmarshal TOML documents from and to data
|
|
||||||
// structures.
|
|
||||||
//
|
|
||||||
// TOML document as a tree
|
|
||||||
//
|
|
||||||
// Go-toml can operate on a TOML document as a tree. Use one of the Load*
|
|
||||||
// functions to parse TOML data and obtain a Tree instance, then one of its
|
|
||||||
// methods to manipulate the tree.
|
|
||||||
//
|
|
||||||
// JSONPath-like queries
|
|
||||||
//
|
|
||||||
// The package github.com/pelletier/go-toml/query implements a system
|
|
||||||
// similar to JSONPath to quickly retrieve elements of a TOML document using a
|
|
||||||
// single expression. See the package documentation for more information.
|
|
||||||
//
|
|
||||||
package toml
|
|
|
@ -1,30 +0,0 @@
|
||||||
# This is a TOML document. Boom.
|
|
||||||
|
|
||||||
title = "TOML Example"
|
|
||||||
|
|
||||||
[owner]
|
|
||||||
name = "Tom Preston-Werner"
|
|
||||||
organization = "GitHub"
|
|
||||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
|
||||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
|
||||||
|
|
||||||
[database]
|
|
||||||
server = "192.168.1.1"
|
|
||||||
ports = [ 8001, 8001, 8002 ]
|
|
||||||
connection_max = 5000
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[servers]
|
|
||||||
|
|
||||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
|
||||||
[servers.alpha]
|
|
||||||
ip = "10.0.0.1"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[servers.beta]
|
|
||||||
ip = "10.0.0.2"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[clients]
|
|
||||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
|
||||||
score = 4e-08 # to make sure leading zeroes in exponent parts of floats are supported
|
|
|
@ -1,30 +0,0 @@
|
||||||
# This is a TOML document. Boom.
|
|
||||||
|
|
||||||
title = "TOML Example"
|
|
||||||
|
|
||||||
[owner]
|
|
||||||
name = "Tom Preston-Werner"
|
|
||||||
organization = "GitHub"
|
|
||||||
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
|
||||||
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
|
||||||
|
|
||||||
[database]
|
|
||||||
server = "192.168.1.1"
|
|
||||||
ports = [ 8001, 8001, 8002 ]
|
|
||||||
connection_max = 5000
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[servers]
|
|
||||||
|
|
||||||
# You can indent as you please. Tabs or spaces. TOML don't care.
|
|
||||||
[servers.alpha]
|
|
||||||
ip = "10.0.0.1"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[servers.beta]
|
|
||||||
ip = "10.0.0.2"
|
|
||||||
dc = "eqdc10"
|
|
||||||
|
|
||||||
[clients]
|
|
||||||
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
|
||||||
score = 4e-08 # to make sure leading zeroes in exponent parts of floats are supported
|
|
|
@ -1,31 +0,0 @@
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
|
||||||
tree, err := LoadBytes(data)
|
|
||||||
if err != nil {
|
|
||||||
if tree != nil {
|
|
||||||
panic("tree must be nil if there is an error")
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
str, err := tree.ToTomlString()
|
|
||||||
if err != nil {
|
|
||||||
if str != "" {
|
|
||||||
panic(`str must be "" if there is an error`)
|
|
||||||
}
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tree, err = Load(str)
|
|
||||||
if err != nil {
|
|
||||||
if tree != nil {
|
|
||||||
panic("tree must be nil if there is an error")
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
#! /bin/sh
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
go get github.com/dvyukov/go-fuzz/go-fuzz
|
|
||||||
go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
|
||||||
|
|
||||||
if [ ! -e toml-fuzz.zip ]; then
|
|
||||||
go-fuzz-build github.com/pelletier/go-toml
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -fr fuzz
|
|
||||||
mkdir -p fuzz/corpus
|
|
||||||
cp *.toml fuzz/corpus
|
|
||||||
|
|
||||||
go-fuzz -bin=toml-fuzz.zip -workdir=fuzz
|
|
|
@ -1,112 +0,0 @@
|
||||||
// Parsing keys handling both bare and quoted keys.
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Convert the bare key group string to an array.
|
|
||||||
// The input supports double quotation and single quotation,
|
|
||||||
// but escape sequences are not supported. Lexers must unescape them beforehand.
|
|
||||||
func parseKey(key string) ([]string, error) {
|
|
||||||
runes := []rune(key)
|
|
||||||
var groups []string
|
|
||||||
|
|
||||||
if len(key) == 0 {
|
|
||||||
return nil, errors.New("empty key")
|
|
||||||
}
|
|
||||||
|
|
||||||
idx := 0
|
|
||||||
for idx < len(runes) {
|
|
||||||
for ; idx < len(runes) && isSpace(runes[idx]); idx++ {
|
|
||||||
// skip leading whitespace
|
|
||||||
}
|
|
||||||
if idx >= len(runes) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r := runes[idx]
|
|
||||||
if isValidBareChar(r) {
|
|
||||||
// parse bare key
|
|
||||||
startIdx := idx
|
|
||||||
endIdx := -1
|
|
||||||
idx++
|
|
||||||
for idx < len(runes) {
|
|
||||||
r = runes[idx]
|
|
||||||
if isValidBareChar(r) {
|
|
||||||
idx++
|
|
||||||
} else if r == '.' {
|
|
||||||
endIdx = idx
|
|
||||||
break
|
|
||||||
} else if isSpace(r) {
|
|
||||||
endIdx = idx
|
|
||||||
for ; idx < len(runes) && isSpace(runes[idx]); idx++ {
|
|
||||||
// skip trailing whitespace
|
|
||||||
}
|
|
||||||
if idx < len(runes) && runes[idx] != '.' {
|
|
||||||
return nil, fmt.Errorf("invalid key character after whitespace: %c", runes[idx])
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("invalid bare key character: %c", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if endIdx == -1 {
|
|
||||||
endIdx = idx
|
|
||||||
}
|
|
||||||
groups = append(groups, string(runes[startIdx:endIdx]))
|
|
||||||
} else if r == '\'' {
|
|
||||||
// parse single quoted key
|
|
||||||
idx++
|
|
||||||
startIdx := idx
|
|
||||||
for {
|
|
||||||
if idx >= len(runes) {
|
|
||||||
return nil, fmt.Errorf("unclosed single-quoted key")
|
|
||||||
}
|
|
||||||
r = runes[idx]
|
|
||||||
if r == '\'' {
|
|
||||||
groups = append(groups, string(runes[startIdx:idx]))
|
|
||||||
idx++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
} else if r == '"' {
|
|
||||||
// parse double quoted key
|
|
||||||
idx++
|
|
||||||
startIdx := idx
|
|
||||||
for {
|
|
||||||
if idx >= len(runes) {
|
|
||||||
return nil, fmt.Errorf("unclosed double-quoted key")
|
|
||||||
}
|
|
||||||
r = runes[idx]
|
|
||||||
if r == '"' {
|
|
||||||
groups = append(groups, string(runes[startIdx:idx]))
|
|
||||||
idx++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
idx++
|
|
||||||
}
|
|
||||||
} else if r == '.' {
|
|
||||||
idx++
|
|
||||||
if idx >= len(runes) {
|
|
||||||
return nil, fmt.Errorf("unexpected end of key")
|
|
||||||
}
|
|
||||||
r = runes[idx]
|
|
||||||
if !isValidBareChar(r) && r != '\'' && r != '"' && r != ' ' {
|
|
||||||
return nil, fmt.Errorf("expecting key part after dot")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("invalid key character: %c", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(groups) == 0 {
|
|
||||||
return nil, fmt.Errorf("empty key")
|
|
||||||
}
|
|
||||||
return groups, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidBareChar(r rune) bool {
|
|
||||||
return isAlphanumeric(r) || r == '-' || isDigit(r)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,287 +0,0 @@
|
||||||
// Implementation of TOML's local date/time.
|
|
||||||
//
|
|
||||||
// Copied over from Google's civil to avoid pulling all the Google dependencies.
|
|
||||||
// Originals:
|
|
||||||
// https://raw.githubusercontent.com/googleapis/google-cloud-go/ed46f5086358513cf8c25f8e3f022cb838a49d66/civil/civil.go
|
|
||||||
// Changes:
|
|
||||||
// * Renamed files from civil* to localtime*.
|
|
||||||
// * Package changed from civil to toml.
|
|
||||||
// * 'Local' prefix added to all structs.
|
|
||||||
//
|
|
||||||
// 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
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,39 +0,0 @@
|
||||||
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"
|
|
|
@ -1,39 +0,0 @@
|
||||||
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"
|
|
|
@ -1,507 +0,0 @@
|
||||||
// TOML Parser.
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tomlParser struct {
|
|
||||||
flowIdx int
|
|
||||||
flow []token
|
|
||||||
tree *Tree
|
|
||||||
currentTable []string
|
|
||||||
seenTableKeys []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type tomlParserStateFn func() tomlParserStateFn
|
|
||||||
|
|
||||||
// Formats and panics an error message based on a token
|
|
||||||
func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) {
|
|
||||||
panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) run() {
|
|
||||||
for state := p.parseStart; state != nil; {
|
|
||||||
state = state()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) peek() *token {
|
|
||||||
if p.flowIdx >= len(p.flow) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &p.flow[p.flowIdx]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) assume(typ tokenType) {
|
|
||||||
tok := p.getToken()
|
|
||||||
if tok == nil {
|
|
||||||
p.raiseError(tok, "was expecting token %s, but token stream is empty", tok)
|
|
||||||
}
|
|
||||||
if tok.typ != typ {
|
|
||||||
p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) getToken() *token {
|
|
||||||
tok := p.peek()
|
|
||||||
if tok == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
p.flowIdx++
|
|
||||||
return tok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseStart() tomlParserStateFn {
|
|
||||||
tok := p.peek()
|
|
||||||
|
|
||||||
// end of stream, parsing is finished
|
|
||||||
if tok == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tok.typ {
|
|
||||||
case tokenDoubleLeftBracket:
|
|
||||||
return p.parseGroupArray
|
|
||||||
case tokenLeftBracket:
|
|
||||||
return p.parseGroup
|
|
||||||
case tokenKey:
|
|
||||||
return p.parseAssign
|
|
||||||
case tokenEOF:
|
|
||||||
return nil
|
|
||||||
case tokenError:
|
|
||||||
p.raiseError(tok, "parsing error: %s", tok.String())
|
|
||||||
default:
|
|
||||||
p.raiseError(tok, "unexpected token %s", tok.typ)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseGroupArray() tomlParserStateFn {
|
|
||||||
startToken := p.getToken() // discard the [[
|
|
||||||
key := p.getToken()
|
|
||||||
if key.typ != tokenKeyGroupArray {
|
|
||||||
p.raiseError(key, "unexpected token %s, was expecting a table array key", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get or create table array element at the indicated part in the path
|
|
||||||
keys, err := parseKey(key.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(key, "invalid table array key: %s", err)
|
|
||||||
}
|
|
||||||
p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries
|
|
||||||
destTree := p.tree.GetPath(keys)
|
|
||||||
var array []*Tree
|
|
||||||
if destTree == nil {
|
|
||||||
array = make([]*Tree, 0)
|
|
||||||
} else if target, ok := destTree.([]*Tree); ok && target != nil {
|
|
||||||
array = destTree.([]*Tree)
|
|
||||||
} else {
|
|
||||||
p.raiseError(key, "key %s is already assigned and not of type table array", key)
|
|
||||||
}
|
|
||||||
p.currentTable = keys
|
|
||||||
|
|
||||||
// add a new tree to the end of the table array
|
|
||||||
newTree := newTree()
|
|
||||||
newTree.position = startToken.Position
|
|
||||||
array = append(array, newTree)
|
|
||||||
p.tree.SetPath(p.currentTable, array)
|
|
||||||
|
|
||||||
// remove all keys that were children of this table array
|
|
||||||
prefix := key.val + "."
|
|
||||||
found := false
|
|
||||||
for ii := 0; ii < len(p.seenTableKeys); {
|
|
||||||
tableKey := p.seenTableKeys[ii]
|
|
||||||
if strings.HasPrefix(tableKey, prefix) {
|
|
||||||
p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...)
|
|
||||||
} else {
|
|
||||||
found = (tableKey == key.val)
|
|
||||||
ii++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep this key name from use by other kinds of assignments
|
|
||||||
if !found {
|
|
||||||
p.seenTableKeys = append(p.seenTableKeys, key.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// move to next parser state
|
|
||||||
p.assume(tokenDoubleRightBracket)
|
|
||||||
return p.parseStart
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseGroup() tomlParserStateFn {
|
|
||||||
startToken := p.getToken() // discard the [
|
|
||||||
key := p.getToken()
|
|
||||||
if key.typ != tokenKeyGroup {
|
|
||||||
p.raiseError(key, "unexpected token %s, was expecting a table key", key)
|
|
||||||
}
|
|
||||||
for _, item := range p.seenTableKeys {
|
|
||||||
if item == key.val {
|
|
||||||
p.raiseError(key, "duplicated tables")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.seenTableKeys = append(p.seenTableKeys, key.val)
|
|
||||||
keys, err := parseKey(key.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(key, "invalid table array key: %s", err)
|
|
||||||
}
|
|
||||||
if err := p.tree.createSubTree(keys, startToken.Position); err != nil {
|
|
||||||
p.raiseError(key, "%s", err)
|
|
||||||
}
|
|
||||||
destTree := p.tree.GetPath(keys)
|
|
||||||
if target, ok := destTree.(*Tree); ok && target != nil && target.inline {
|
|
||||||
p.raiseError(key, "could not re-define exist inline table or its sub-table : %s",
|
|
||||||
strings.Join(keys, "."))
|
|
||||||
}
|
|
||||||
p.assume(tokenRightBracket)
|
|
||||||
p.currentTable = keys
|
|
||||||
return p.parseStart
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseAssign() tomlParserStateFn {
|
|
||||||
key := p.getToken()
|
|
||||||
p.assume(tokenEqual)
|
|
||||||
|
|
||||||
parsedKey, err := parseKey(key.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(key, "invalid key: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
value := p.parseRvalue()
|
|
||||||
var tableKey []string
|
|
||||||
if len(p.currentTable) > 0 {
|
|
||||||
tableKey = p.currentTable
|
|
||||||
} else {
|
|
||||||
tableKey = []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixKey := parsedKey[0 : len(parsedKey)-1]
|
|
||||||
tableKey = append(tableKey, prefixKey...)
|
|
||||||
|
|
||||||
// find the table to assign, looking out for arrays of tables
|
|
||||||
var targetNode *Tree
|
|
||||||
switch node := p.tree.GetPath(tableKey).(type) {
|
|
||||||
case []*Tree:
|
|
||||||
targetNode = node[len(node)-1]
|
|
||||||
case *Tree:
|
|
||||||
targetNode = node
|
|
||||||
case nil:
|
|
||||||
// create intermediate
|
|
||||||
if err := p.tree.createSubTree(tableKey, key.Position); err != nil {
|
|
||||||
p.raiseError(key, "could not create intermediate group: %s", err)
|
|
||||||
}
|
|
||||||
targetNode = p.tree.GetPath(tableKey).(*Tree)
|
|
||||||
default:
|
|
||||||
p.raiseError(key, "Unknown table type for path: %s",
|
|
||||||
strings.Join(tableKey, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
if targetNode.inline {
|
|
||||||
p.raiseError(key, "could not add key or sub-table to exist inline table or its sub-table : %s",
|
|
||||||
strings.Join(tableKey, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign value to the found table
|
|
||||||
keyVal := parsedKey[len(parsedKey)-1]
|
|
||||||
localKey := []string{keyVal}
|
|
||||||
finalKey := append(tableKey, keyVal)
|
|
||||||
if targetNode.GetPath(localKey) != nil {
|
|
||||||
p.raiseError(key, "The following key was defined twice: %s",
|
|
||||||
strings.Join(finalKey, "."))
|
|
||||||
}
|
|
||||||
var toInsert interface{}
|
|
||||||
|
|
||||||
switch value.(type) {
|
|
||||||
case *Tree, []*Tree:
|
|
||||||
toInsert = value
|
|
||||||
default:
|
|
||||||
toInsert = &tomlValue{value: value, position: key.Position}
|
|
||||||
}
|
|
||||||
targetNode.values[keyVal] = toInsert
|
|
||||||
return p.parseStart
|
|
||||||
}
|
|
||||||
|
|
||||||
var errInvalidUnderscore = errors.New("invalid use of _ in number")
|
|
||||||
|
|
||||||
func numberContainsInvalidUnderscore(value string) error {
|
|
||||||
// For large numbers, you may use underscores between digits to enhance
|
|
||||||
// readability. Each underscore must be surrounded by at least one digit on
|
|
||||||
// each side.
|
|
||||||
|
|
||||||
hasBefore := false
|
|
||||||
for idx, r := range value {
|
|
||||||
if r == '_' {
|
|
||||||
if !hasBefore || idx+1 >= len(value) {
|
|
||||||
// can't end with an underscore
|
|
||||||
return errInvalidUnderscore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hasBefore = isDigit(r)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var errInvalidUnderscoreHex = errors.New("invalid use of _ in hex number")
|
|
||||||
|
|
||||||
func hexNumberContainsInvalidUnderscore(value string) error {
|
|
||||||
hasBefore := false
|
|
||||||
for idx, r := range value {
|
|
||||||
if r == '_' {
|
|
||||||
if !hasBefore || idx+1 >= len(value) {
|
|
||||||
// can't end with an underscore
|
|
||||||
return errInvalidUnderscoreHex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hasBefore = isHexDigit(r)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanupNumberToken(value string) string {
|
|
||||||
cleanedVal := strings.Replace(value, "_", "", -1)
|
|
||||||
return cleanedVal
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseRvalue() interface{} {
|
|
||||||
tok := p.getToken()
|
|
||||||
if tok == nil || tok.typ == tokenEOF {
|
|
||||||
p.raiseError(tok, "expecting a value")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tok.typ {
|
|
||||||
case tokenString:
|
|
||||||
return tok.val
|
|
||||||
case tokenTrue:
|
|
||||||
return true
|
|
||||||
case tokenFalse:
|
|
||||||
return false
|
|
||||||
case tokenInf:
|
|
||||||
if tok.val[0] == '-' {
|
|
||||||
return math.Inf(-1)
|
|
||||||
}
|
|
||||||
return math.Inf(1)
|
|
||||||
case tokenNan:
|
|
||||||
return math.NaN()
|
|
||||||
case tokenInteger:
|
|
||||||
cleanedVal := cleanupNumberToken(tok.val)
|
|
||||||
base := 10
|
|
||||||
s := cleanedVal
|
|
||||||
checkInvalidUnderscore := numberContainsInvalidUnderscore
|
|
||||||
if len(cleanedVal) >= 3 && cleanedVal[0] == '0' {
|
|
||||||
switch cleanedVal[1] {
|
|
||||||
case 'x':
|
|
||||||
checkInvalidUnderscore = hexNumberContainsInvalidUnderscore
|
|
||||||
base = 16
|
|
||||||
case 'o':
|
|
||||||
base = 8
|
|
||||||
case 'b':
|
|
||||||
base = 2
|
|
||||||
default:
|
|
||||||
panic("invalid base") // the lexer should catch this first
|
|
||||||
}
|
|
||||||
s = cleanedVal[2:]
|
|
||||||
}
|
|
||||||
|
|
||||||
err := checkInvalidUnderscore(tok.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var val interface{}
|
|
||||||
val, err = strconv.ParseInt(s, base, 64)
|
|
||||||
if err == nil {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
if s[0] != '-' {
|
|
||||||
if val, err = strconv.ParseUint(s, base, 64); err == nil {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
case tokenFloat:
|
|
||||||
err := numberContainsInvalidUnderscore(tok.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
cleanedVal := cleanupNumberToken(tok.val)
|
|
||||||
val, err := strconv.ParseFloat(cleanedVal, 64)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
case tokenLocalTime:
|
|
||||||
val, err := ParseLocalTime(tok.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
case tokenLocalDate:
|
|
||||||
// a local date may be followed by:
|
|
||||||
// * nothing: this is a local date
|
|
||||||
// * a local time: this is a local date-time
|
|
||||||
|
|
||||||
next := p.peek()
|
|
||||||
if next == nil || next.typ != tokenLocalTime {
|
|
||||||
val, err := ParseLocalDate(tok.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
localDate := tok
|
|
||||||
localTime := p.getToken()
|
|
||||||
|
|
||||||
next = p.peek()
|
|
||||||
if next == nil || next.typ != tokenTimeOffset {
|
|
||||||
v := localDate.val + "T" + localTime.val
|
|
||||||
val, err := ParseLocalDateTime(v)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := p.getToken()
|
|
||||||
|
|
||||||
layout := time.RFC3339Nano
|
|
||||||
v := localDate.val + "T" + localTime.val + offset.val
|
|
||||||
val, err := time.ParseInLocation(layout, v, time.UTC)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(tok, "%s", err)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
case tokenLeftBracket:
|
|
||||||
return p.parseArray()
|
|
||||||
case tokenLeftCurlyBrace:
|
|
||||||
return p.parseInlineTable()
|
|
||||||
case tokenEqual:
|
|
||||||
p.raiseError(tok, "cannot have multiple equals for the same key")
|
|
||||||
case tokenError:
|
|
||||||
p.raiseError(tok, "%s", tok)
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unhandled token: %v", tok))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func tokenIsComma(t *token) bool {
|
|
||||||
return t != nil && t.typ == tokenComma
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseInlineTable() *Tree {
|
|
||||||
tree := newTree()
|
|
||||||
var previous *token
|
|
||||||
Loop:
|
|
||||||
for {
|
|
||||||
follow := p.peek()
|
|
||||||
if follow == nil || follow.typ == tokenEOF {
|
|
||||||
p.raiseError(follow, "unterminated inline table")
|
|
||||||
}
|
|
||||||
switch follow.typ {
|
|
||||||
case tokenRightCurlyBrace:
|
|
||||||
p.getToken()
|
|
||||||
break Loop
|
|
||||||
case tokenKey, tokenInteger, tokenString:
|
|
||||||
if !tokenIsComma(previous) && previous != nil {
|
|
||||||
p.raiseError(follow, "comma expected between fields in inline table")
|
|
||||||
}
|
|
||||||
key := p.getToken()
|
|
||||||
p.assume(tokenEqual)
|
|
||||||
|
|
||||||
parsedKey, err := parseKey(key.val)
|
|
||||||
if err != nil {
|
|
||||||
p.raiseError(key, "invalid key: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
value := p.parseRvalue()
|
|
||||||
tree.SetPath(parsedKey, value)
|
|
||||||
case tokenComma:
|
|
||||||
if tokenIsComma(previous) {
|
|
||||||
p.raiseError(follow, "need field between two commas in inline table")
|
|
||||||
}
|
|
||||||
p.getToken()
|
|
||||||
default:
|
|
||||||
p.raiseError(follow, "unexpected token type in inline table: %s", follow.String())
|
|
||||||
}
|
|
||||||
previous = follow
|
|
||||||
}
|
|
||||||
if tokenIsComma(previous) {
|
|
||||||
p.raiseError(previous, "trailing comma at the end of inline table")
|
|
||||||
}
|
|
||||||
tree.inline = true
|
|
||||||
return tree
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tomlParser) parseArray() interface{} {
|
|
||||||
var array []interface{}
|
|
||||||
arrayType := reflect.TypeOf(newTree())
|
|
||||||
for {
|
|
||||||
follow := p.peek()
|
|
||||||
if follow == nil || follow.typ == tokenEOF {
|
|
||||||
p.raiseError(follow, "unterminated array")
|
|
||||||
}
|
|
||||||
if follow.typ == tokenRightBracket {
|
|
||||||
p.getToken()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
val := p.parseRvalue()
|
|
||||||
if reflect.TypeOf(val) != arrayType {
|
|
||||||
arrayType = nil
|
|
||||||
}
|
|
||||||
array = append(array, val)
|
|
||||||
follow = p.peek()
|
|
||||||
if follow == nil || follow.typ == tokenEOF {
|
|
||||||
p.raiseError(follow, "unterminated array")
|
|
||||||
}
|
|
||||||
if follow.typ != tokenRightBracket && follow.typ != tokenComma {
|
|
||||||
p.raiseError(follow, "missing comma")
|
|
||||||
}
|
|
||||||
if follow.typ == tokenComma {
|
|
||||||
p.getToken()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the array is a mixed-type array or its length is 0,
|
|
||||||
// don't convert it to a table array
|
|
||||||
if len(array) <= 0 {
|
|
||||||
arrayType = nil
|
|
||||||
}
|
|
||||||
// An array of Trees is actually an array of inline
|
|
||||||
// tables, which is a shorthand for a table array. If the
|
|
||||||
// array was not converted from []interface{} to []*Tree,
|
|
||||||
// the two notations would not be equivalent.
|
|
||||||
if arrayType == reflect.TypeOf(newTree()) {
|
|
||||||
tomlArray := make([]*Tree, len(array))
|
|
||||||
for i, v := range array {
|
|
||||||
tomlArray[i] = v.(*Tree)
|
|
||||||
}
|
|
||||||
return tomlArray
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseToml(flow []token) *Tree {
|
|
||||||
result := newTree()
|
|
||||||
result.position = Position{1, 1}
|
|
||||||
parser := &tomlParser{
|
|
||||||
flowIdx: 0,
|
|
||||||
flow: flow,
|
|
||||||
tree: result,
|
|
||||||
currentTable: make([]string, 0),
|
|
||||||
seenTableKeys: make([]string, 0),
|
|
||||||
}
|
|
||||||
parser.run()
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Position support for go-toml
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Position of a document element within a TOML document.
|
|
||||||
//
|
|
||||||
// Line and Col are both 1-indexed positions for the element's line number and
|
|
||||||
// column number, respectively. Values of zero or less will cause Invalid(),
|
|
||||||
// to return true.
|
|
||||||
type Position struct {
|
|
||||||
Line int // line within the document
|
|
||||||
Col int // column within the line
|
|
||||||
}
|
|
||||||
|
|
||||||
// String representation of the position.
|
|
||||||
// Displays 1-indexed line and column numbers.
|
|
||||||
func (p Position) String() string {
|
|
||||||
return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid returns whether or not the position is valid (i.e. with negative or
|
|
||||||
// null values)
|
|
||||||
func (p Position) Invalid() bool {
|
|
||||||
return p.Line <= 0 || p.Col <= 0
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Define tokens
|
|
||||||
type tokenType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
eof = -(iota + 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
tokenError tokenType = iota
|
|
||||||
tokenEOF
|
|
||||||
tokenComment
|
|
||||||
tokenKey
|
|
||||||
tokenString
|
|
||||||
tokenInteger
|
|
||||||
tokenTrue
|
|
||||||
tokenFalse
|
|
||||||
tokenFloat
|
|
||||||
tokenInf
|
|
||||||
tokenNan
|
|
||||||
tokenEqual
|
|
||||||
tokenLeftBracket
|
|
||||||
tokenRightBracket
|
|
||||||
tokenLeftCurlyBrace
|
|
||||||
tokenRightCurlyBrace
|
|
||||||
tokenLeftParen
|
|
||||||
tokenRightParen
|
|
||||||
tokenDoubleLeftBracket
|
|
||||||
tokenDoubleRightBracket
|
|
||||||
tokenLocalDate
|
|
||||||
tokenLocalTime
|
|
||||||
tokenTimeOffset
|
|
||||||
tokenKeyGroup
|
|
||||||
tokenKeyGroupArray
|
|
||||||
tokenComma
|
|
||||||
tokenColon
|
|
||||||
tokenDollar
|
|
||||||
tokenStar
|
|
||||||
tokenQuestion
|
|
||||||
tokenDot
|
|
||||||
tokenDotDot
|
|
||||||
tokenEOL
|
|
||||||
)
|
|
||||||
|
|
||||||
var tokenTypeNames = []string{
|
|
||||||
"Error",
|
|
||||||
"EOF",
|
|
||||||
"Comment",
|
|
||||||
"Key",
|
|
||||||
"String",
|
|
||||||
"Integer",
|
|
||||||
"True",
|
|
||||||
"False",
|
|
||||||
"Float",
|
|
||||||
"Inf",
|
|
||||||
"NaN",
|
|
||||||
"=",
|
|
||||||
"[",
|
|
||||||
"]",
|
|
||||||
"{",
|
|
||||||
"}",
|
|
||||||
"(",
|
|
||||||
")",
|
|
||||||
"]]",
|
|
||||||
"[[",
|
|
||||||
"LocalDate",
|
|
||||||
"LocalTime",
|
|
||||||
"TimeOffset",
|
|
||||||
"KeyGroup",
|
|
||||||
"KeyGroupArray",
|
|
||||||
",",
|
|
||||||
":",
|
|
||||||
"$",
|
|
||||||
"*",
|
|
||||||
"?",
|
|
||||||
".",
|
|
||||||
"..",
|
|
||||||
"EOL",
|
|
||||||
}
|
|
||||||
|
|
||||||
type token struct {
|
|
||||||
Position
|
|
||||||
typ tokenType
|
|
||||||
val string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tt tokenType) String() string {
|
|
||||||
idx := int(tt)
|
|
||||||
if idx < len(tokenTypeNames) {
|
|
||||||
return tokenTypeNames[idx]
|
|
||||||
}
|
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t token) String() string {
|
|
||||||
switch t.typ {
|
|
||||||
case tokenEOF:
|
|
||||||
return "EOF"
|
|
||||||
case tokenError:
|
|
||||||
return t.val
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%q", t.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSpace(r rune) bool {
|
|
||||||
return r == ' ' || r == '\t'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAlphanumeric(r rune) bool {
|
|
||||||
return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || r == '_'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isKeyChar(r rune) bool {
|
|
||||||
// Keys start with the first character that isn't whitespace or [ and end
|
|
||||||
// with the last non-whitespace character before the equals sign. Keys
|
|
||||||
// cannot contain a # character."
|
|
||||||
return !(r == '\r' || r == '\n' || r == eof || r == '=')
|
|
||||||
}
|
|
||||||
|
|
||||||
func isKeyStartChar(r rune) bool {
|
|
||||||
return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '[')
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDigit(r rune) bool {
|
|
||||||
return '0' <= r && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
func isHexDigit(r rune) bool {
|
|
||||||
return isDigit(r) ||
|
|
||||||
(r >= 'a' && r <= 'f') ||
|
|
||||||
(r >= 'A' && r <= 'F')
|
|
||||||
}
|
|
|
@ -1,533 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tomlValue struct {
|
|
||||||
value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
|
|
||||||
comment string
|
|
||||||
commented bool
|
|
||||||
multiline bool
|
|
||||||
literal bool
|
|
||||||
position Position
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tree is the result of the parsing of a TOML file.
|
|
||||||
type Tree struct {
|
|
||||||
values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
|
|
||||||
comment string
|
|
||||||
commented bool
|
|
||||||
inline bool
|
|
||||||
position Position
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTree() *Tree {
|
|
||||||
return newTreeWithPosition(Position{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTreeWithPosition(pos Position) *Tree {
|
|
||||||
return &Tree{
|
|
||||||
values: make(map[string]interface{}),
|
|
||||||
position: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TreeFromMap initializes a new Tree object using the given map.
|
|
||||||
func TreeFromMap(m map[string]interface{}) (*Tree, error) {
|
|
||||||
result, err := toTree(m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result.(*Tree), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position returns the position of the tree.
|
|
||||||
func (t *Tree) Position() Position {
|
|
||||||
return t.position
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has returns a boolean indicating if the given key exists.
|
|
||||||
func (t *Tree) Has(key string) bool {
|
|
||||||
if key == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return t.HasPath(strings.Split(key, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasPath returns true if the given path of keys exists, false otherwise.
|
|
||||||
func (t *Tree) HasPath(keys []string) bool {
|
|
||||||
return t.GetPath(keys) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns the keys of the toplevel tree (does not recurse).
|
|
||||||
func (t *Tree) Keys() []string {
|
|
||||||
keys := make([]string, len(t.values))
|
|
||||||
i := 0
|
|
||||||
for k := range t.values {
|
|
||||||
keys[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the value at key in the Tree.
|
|
||||||
// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
|
|
||||||
// If you need to retrieve non-bare keys, use GetPath.
|
|
||||||
// Returns nil if the path does not exist in the tree.
|
|
||||||
// If keys is of length zero, the current tree is returned.
|
|
||||||
func (t *Tree) Get(key string) interface{} {
|
|
||||||
if key == "" {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return t.GetPath(strings.Split(key, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPath returns the element in the tree indicated by 'keys'.
|
|
||||||
// If keys is of length zero, the current tree is returned.
|
|
||||||
func (t *Tree) GetPath(keys []string) interface{} {
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
subtree := t
|
|
||||||
for _, intermediateKey := range keys[:len(keys)-1] {
|
|
||||||
value, exists := subtree.values[intermediateKey]
|
|
||||||
if !exists {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch node := value.(type) {
|
|
||||||
case *Tree:
|
|
||||||
subtree = node
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
subtree = node[len(node)-1]
|
|
||||||
default:
|
|
||||||
return nil // cannot navigate through other node types
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// branch based on final node type
|
|
||||||
switch node := subtree.values[keys[len(keys)-1]].(type) {
|
|
||||||
case *tomlValue:
|
|
||||||
return node.value
|
|
||||||
default:
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArray returns the value at key in the Tree.
|
|
||||||
// It returns []string, []int64, etc type if key has homogeneous lists
|
|
||||||
// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings.
|
|
||||||
// Returns nil if the path does not exist in the tree.
|
|
||||||
// If keys is of length zero, the current tree is returned.
|
|
||||||
func (t *Tree) GetArray(key string) interface{} {
|
|
||||||
if key == "" {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return t.GetArrayPath(strings.Split(key, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArrayPath returns the element in the tree indicated by 'keys'.
|
|
||||||
// If keys is of length zero, the current tree is returned.
|
|
||||||
func (t *Tree) GetArrayPath(keys []string) interface{} {
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
subtree := t
|
|
||||||
for _, intermediateKey := range keys[:len(keys)-1] {
|
|
||||||
value, exists := subtree.values[intermediateKey]
|
|
||||||
if !exists {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch node := value.(type) {
|
|
||||||
case *Tree:
|
|
||||||
subtree = node
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
subtree = node[len(node)-1]
|
|
||||||
default:
|
|
||||||
return nil // cannot navigate through other node types
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// branch based on final node type
|
|
||||||
switch node := subtree.values[keys[len(keys)-1]].(type) {
|
|
||||||
case *tomlValue:
|
|
||||||
switch n := node.value.(type) {
|
|
||||||
case []interface{}:
|
|
||||||
return getArray(n)
|
|
||||||
default:
|
|
||||||
return node.value
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if homogeneous array, then return slice type object over []interface{}
|
|
||||||
func getArray(n []interface{}) interface{} {
|
|
||||||
var s []string
|
|
||||||
var i64 []int64
|
|
||||||
var f64 []float64
|
|
||||||
var bl []bool
|
|
||||||
for _, value := range n {
|
|
||||||
switch v := value.(type) {
|
|
||||||
case string:
|
|
||||||
s = append(s, v)
|
|
||||||
case int64:
|
|
||||||
i64 = append(i64, v)
|
|
||||||
case float64:
|
|
||||||
f64 = append(f64, v)
|
|
||||||
case bool:
|
|
||||||
bl = append(bl, v)
|
|
||||||
default:
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(s) == len(n) {
|
|
||||||
return s
|
|
||||||
} else if len(i64) == len(n) {
|
|
||||||
return i64
|
|
||||||
} else if len(f64) == len(n) {
|
|
||||||
return f64
|
|
||||||
} else if len(bl) == len(n) {
|
|
||||||
return bl
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPosition returns the position of the given key.
|
|
||||||
func (t *Tree) GetPosition(key string) Position {
|
|
||||||
if key == "" {
|
|
||||||
return t.position
|
|
||||||
}
|
|
||||||
return t.GetPositionPath(strings.Split(key, "."))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPositionPath sets the position of element in the tree indicated by 'keys'.
|
|
||||||
// If keys is of length zero, the current tree position is set.
|
|
||||||
func (t *Tree) SetPositionPath(keys []string, pos Position) {
|
|
||||||
if len(keys) == 0 {
|
|
||||||
t.position = pos
|
|
||||||
return
|
|
||||||
}
|
|
||||||
subtree := t
|
|
||||||
for _, intermediateKey := range keys[:len(keys)-1] {
|
|
||||||
value, exists := subtree.values[intermediateKey]
|
|
||||||
if !exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch node := value.(type) {
|
|
||||||
case *Tree:
|
|
||||||
subtree = node
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
subtree = node[len(node)-1]
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// branch based on final node type
|
|
||||||
switch node := subtree.values[keys[len(keys)-1]].(type) {
|
|
||||||
case *tomlValue:
|
|
||||||
node.position = pos
|
|
||||||
return
|
|
||||||
case *Tree:
|
|
||||||
node.position = pos
|
|
||||||
return
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node[len(node)-1].position = pos
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPositionPath returns the element in the tree indicated by 'keys'.
|
|
||||||
// If keys is of length zero, the current tree is returned.
|
|
||||||
func (t *Tree) GetPositionPath(keys []string) Position {
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return t.position
|
|
||||||
}
|
|
||||||
subtree := t
|
|
||||||
for _, intermediateKey := range keys[:len(keys)-1] {
|
|
||||||
value, exists := subtree.values[intermediateKey]
|
|
||||||
if !exists {
|
|
||||||
return Position{0, 0}
|
|
||||||
}
|
|
||||||
switch node := value.(type) {
|
|
||||||
case *Tree:
|
|
||||||
subtree = node
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
return Position{0, 0}
|
|
||||||
}
|
|
||||||
subtree = node[len(node)-1]
|
|
||||||
default:
|
|
||||||
return Position{0, 0}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// branch based on final node type
|
|
||||||
switch node := subtree.values[keys[len(keys)-1]].(type) {
|
|
||||||
case *tomlValue:
|
|
||||||
return node.position
|
|
||||||
case *Tree:
|
|
||||||
return node.position
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
return Position{0, 0}
|
|
||||||
}
|
|
||||||
return node[len(node)-1].position
|
|
||||||
default:
|
|
||||||
return Position{0, 0}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDefault works like Get but with a default value
|
|
||||||
func (t *Tree) GetDefault(key string, def interface{}) interface{} {
|
|
||||||
val := t.Get(key)
|
|
||||||
if val == nil {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOptions arguments are supplied to the SetWithOptions and SetPathWithOptions functions to modify marshalling behaviour.
|
|
||||||
// The default values within the struct are valid default options.
|
|
||||||
type SetOptions struct {
|
|
||||||
Comment string
|
|
||||||
Commented bool
|
|
||||||
Multiline bool
|
|
||||||
Literal bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetWithOptions is the same as Set, but allows you to provide formatting
|
|
||||||
// instructions to the key, that will be used by Marshal().
|
|
||||||
func (t *Tree) SetWithOptions(key string, opts SetOptions, value interface{}) {
|
|
||||||
t.SetPathWithOptions(strings.Split(key, "."), opts, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPathWithOptions is the same as SetPath, but allows you to provide
|
|
||||||
// formatting instructions to the key, that will be reused by Marshal().
|
|
||||||
func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interface{}) {
|
|
||||||
subtree := t
|
|
||||||
for i, intermediateKey := range keys[:len(keys)-1] {
|
|
||||||
nextTree, exists := subtree.values[intermediateKey]
|
|
||||||
if !exists {
|
|
||||||
nextTree = newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
|
|
||||||
subtree.values[intermediateKey] = nextTree // add new element here
|
|
||||||
}
|
|
||||||
switch node := nextTree.(type) {
|
|
||||||
case *Tree:
|
|
||||||
subtree = node
|
|
||||||
case []*Tree:
|
|
||||||
// go to most recent element
|
|
||||||
if len(node) == 0 {
|
|
||||||
// create element if it does not exist
|
|
||||||
node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
|
|
||||||
subtree.values[intermediateKey] = node
|
|
||||||
}
|
|
||||||
subtree = node[len(node)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var toInsert interface{}
|
|
||||||
|
|
||||||
switch v := value.(type) {
|
|
||||||
case *Tree:
|
|
||||||
v.comment = opts.Comment
|
|
||||||
v.commented = opts.Commented
|
|
||||||
toInsert = value
|
|
||||||
case []*Tree:
|
|
||||||
for i := range v {
|
|
||||||
v[i].commented = opts.Commented
|
|
||||||
}
|
|
||||||
toInsert = value
|
|
||||||
case *tomlValue:
|
|
||||||
v.comment = opts.Comment
|
|
||||||
v.commented = opts.Commented
|
|
||||||
v.multiline = opts.Multiline
|
|
||||||
v.literal = opts.Literal
|
|
||||||
toInsert = v
|
|
||||||
default:
|
|
||||||
toInsert = &tomlValue{value: value,
|
|
||||||
comment: opts.Comment,
|
|
||||||
commented: opts.Commented,
|
|
||||||
multiline: opts.Multiline,
|
|
||||||
literal: opts.Literal,
|
|
||||||
position: Position{Line: subtree.position.Line + len(subtree.values) + 1, Col: subtree.position.Col}}
|
|
||||||
}
|
|
||||||
|
|
||||||
subtree.values[keys[len(keys)-1]] = toInsert
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set an element in the tree.
|
|
||||||
// Key is a dot-separated path (e.g. a.b.c).
|
|
||||||
// Creates all necessary intermediate trees, if needed.
|
|
||||||
func (t *Tree) Set(key string, value interface{}) {
|
|
||||||
t.SetWithComment(key, "", false, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetWithComment is the same as Set, but allows you to provide comment
|
|
||||||
// information to the key, that will be reused by Marshal().
|
|
||||||
func (t *Tree) SetWithComment(key string, comment string, commented bool, value interface{}) {
|
|
||||||
t.SetPathWithComment(strings.Split(key, "."), comment, commented, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPath sets an element in the tree.
|
|
||||||
// Keys is an array of path elements (e.g. {"a","b","c"}).
|
|
||||||
// Creates all necessary intermediate trees, if needed.
|
|
||||||
func (t *Tree) SetPath(keys []string, value interface{}) {
|
|
||||||
t.SetPathWithComment(keys, "", false, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPathWithComment is the same as SetPath, but allows you to provide comment
|
|
||||||
// information to the key, that will be reused by Marshal().
|
|
||||||
func (t *Tree) SetPathWithComment(keys []string, comment string, commented bool, value interface{}) {
|
|
||||||
t.SetPathWithOptions(keys, SetOptions{Comment: comment, Commented: commented}, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes a key from the tree.
|
|
||||||
// Key is a dot-separated path (e.g. a.b.c).
|
|
||||||
func (t *Tree) Delete(key string) error {
|
|
||||||
keys, err := parseKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return t.DeletePath(keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePath removes a key from the tree.
|
|
||||||
// Keys is an array of path elements (e.g. {"a","b","c"}).
|
|
||||||
func (t *Tree) DeletePath(keys []string) error {
|
|
||||||
keyLen := len(keys)
|
|
||||||
if keyLen == 1 {
|
|
||||||
delete(t.values, keys[0])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
tree := t.GetPath(keys[:keyLen-1])
|
|
||||||
item := keys[keyLen-1]
|
|
||||||
switch node := tree.(type) {
|
|
||||||
case *Tree:
|
|
||||||
delete(node.values, item)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("no such key to delete")
|
|
||||||
}
|
|
||||||
|
|
||||||
// createSubTree takes a tree and a key and create the necessary intermediate
|
|
||||||
// subtrees to create a subtree at that point. In-place.
|
|
||||||
//
|
|
||||||
// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
|
|
||||||
// and tree[a][b][c]
|
|
||||||
//
|
|
||||||
// Returns nil on success, error object on failure
|
|
||||||
func (t *Tree) createSubTree(keys []string, pos Position) error {
|
|
||||||
subtree := t
|
|
||||||
for i, intermediateKey := range keys {
|
|
||||||
nextTree, exists := subtree.values[intermediateKey]
|
|
||||||
if !exists {
|
|
||||||
tree := newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})
|
|
||||||
tree.position = pos
|
|
||||||
tree.inline = subtree.inline
|
|
||||||
subtree.values[intermediateKey] = tree
|
|
||||||
nextTree = tree
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node := nextTree.(type) {
|
|
||||||
case []*Tree:
|
|
||||||
subtree = node[len(node)-1]
|
|
||||||
case *Tree:
|
|
||||||
subtree = node
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
|
|
||||||
strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadBytes creates a Tree from a []byte.
|
|
||||||
func LoadBytes(b []byte) (tree *Tree, err error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if _, ok := r.(runtime.Error); ok {
|
|
||||||
panic(r)
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("%s", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if len(b) >= 4 && (hasUTF32BigEndianBOM4(b) || hasUTF32LittleEndianBOM4(b)) {
|
|
||||||
b = b[4:]
|
|
||||||
} else if len(b) >= 3 && hasUTF8BOM3(b) {
|
|
||||||
b = b[3:]
|
|
||||||
} else if len(b) >= 2 && (hasUTF16BigEndianBOM2(b) || hasUTF16LittleEndianBOM2(b)) {
|
|
||||||
b = b[2:]
|
|
||||||
}
|
|
||||||
|
|
||||||
tree = parseToml(lexToml(b))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasUTF16BigEndianBOM2(b []byte) bool {
|
|
||||||
return b[0] == 0xFE && b[1] == 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasUTF16LittleEndianBOM2(b []byte) bool {
|
|
||||||
return b[0] == 0xFF && b[1] == 0xFE
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasUTF8BOM3(b []byte) bool {
|
|
||||||
return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasUTF32BigEndianBOM4(b []byte) bool {
|
|
||||||
return b[0] == 0x00 && b[1] == 0x00 && b[2] == 0xFE && b[3] == 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasUTF32LittleEndianBOM4(b []byte) bool {
|
|
||||||
return b[0] == 0xFF && b[1] == 0xFE && b[2] == 0x00 && b[3] == 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadReader creates a Tree from any io.Reader.
|
|
||||||
func LoadReader(reader io.Reader) (tree *Tree, err error) {
|
|
||||||
inputBytes, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tree, err = LoadBytes(inputBytes)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load creates a Tree from a string.
|
|
||||||
func Load(content string) (tree *Tree, err error) {
|
|
||||||
return LoadBytes([]byte(content))
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadFile creates a Tree from a file.
|
|
||||||
func LoadFile(path string) (tree *Tree, err error) {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
return LoadReader(file)
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
// PubTOMLValue wrapping tomlValue in order to access all properties from outside.
|
|
||||||
type PubTOMLValue = tomlValue
|
|
||||||
|
|
||||||
func (ptv *PubTOMLValue) Value() interface{} {
|
|
||||||
return ptv.value
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) Comment() string {
|
|
||||||
return ptv.comment
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) Commented() bool {
|
|
||||||
return ptv.commented
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) Multiline() bool {
|
|
||||||
return ptv.multiline
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) Position() Position {
|
|
||||||
return ptv.position
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ptv *PubTOMLValue) SetValue(v interface{}) {
|
|
||||||
ptv.value = v
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) SetComment(s string) {
|
|
||||||
ptv.comment = s
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) SetCommented(c bool) {
|
|
||||||
ptv.commented = c
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) SetMultiline(m bool) {
|
|
||||||
ptv.multiline = m
|
|
||||||
}
|
|
||||||
func (ptv *PubTOMLValue) SetPosition(p Position) {
|
|
||||||
ptv.position = p
|
|
||||||
}
|
|
||||||
|
|
||||||
// PubTree wrapping Tree in order to access all properties from outside.
|
|
||||||
type PubTree = Tree
|
|
||||||
|
|
||||||
func (pt *PubTree) Values() map[string]interface{} {
|
|
||||||
return pt.values
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) Comment() string {
|
|
||||||
return pt.comment
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) Commented() bool {
|
|
||||||
return pt.commented
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) Inline() bool {
|
|
||||||
return pt.inline
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) SetValues(v map[string]interface{}) {
|
|
||||||
pt.values = v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) SetComment(c string) {
|
|
||||||
pt.comment = c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) SetCommented(c bool) {
|
|
||||||
pt.commented = c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pt *PubTree) SetInline(i bool) {
|
|
||||||
pt.inline = i
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var kindToType = [reflect.String + 1]reflect.Type{
|
|
||||||
reflect.Bool: reflect.TypeOf(true),
|
|
||||||
reflect.String: reflect.TypeOf(""),
|
|
||||||
reflect.Float32: reflect.TypeOf(float64(1)),
|
|
||||||
reflect.Float64: reflect.TypeOf(float64(1)),
|
|
||||||
reflect.Int: reflect.TypeOf(int64(1)),
|
|
||||||
reflect.Int8: reflect.TypeOf(int64(1)),
|
|
||||||
reflect.Int16: reflect.TypeOf(int64(1)),
|
|
||||||
reflect.Int32: reflect.TypeOf(int64(1)),
|
|
||||||
reflect.Int64: reflect.TypeOf(int64(1)),
|
|
||||||
reflect.Uint: reflect.TypeOf(uint64(1)),
|
|
||||||
reflect.Uint8: reflect.TypeOf(uint64(1)),
|
|
||||||
reflect.Uint16: reflect.TypeOf(uint64(1)),
|
|
||||||
reflect.Uint32: reflect.TypeOf(uint64(1)),
|
|
||||||
reflect.Uint64: reflect.TypeOf(uint64(1)),
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found.
|
|
||||||
// supported values:
|
|
||||||
// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32
|
|
||||||
func typeFor(k reflect.Kind) reflect.Type {
|
|
||||||
if k > 0 && int(k) < len(kindToType) {
|
|
||||||
return kindToType[k]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func simpleValueCoercion(object interface{}) (interface{}, error) {
|
|
||||||
switch original := object.(type) {
|
|
||||||
case string, bool, int64, uint64, float64, time.Time:
|
|
||||||
return original, nil
|
|
||||||
case int:
|
|
||||||
return int64(original), nil
|
|
||||||
case int8:
|
|
||||||
return int64(original), nil
|
|
||||||
case int16:
|
|
||||||
return int64(original), nil
|
|
||||||
case int32:
|
|
||||||
return int64(original), nil
|
|
||||||
case uint:
|
|
||||||
return uint64(original), nil
|
|
||||||
case uint8:
|
|
||||||
return uint64(original), nil
|
|
||||||
case uint16:
|
|
||||||
return uint64(original), nil
|
|
||||||
case uint32:
|
|
||||||
return uint64(original), nil
|
|
||||||
case float32:
|
|
||||||
return float64(original), nil
|
|
||||||
case fmt.Stringer:
|
|
||||||
return original.String(), nil
|
|
||||||
case []interface{}:
|
|
||||||
value := reflect.ValueOf(original)
|
|
||||||
length := value.Len()
|
|
||||||
arrayValue := reflect.MakeSlice(value.Type(), 0, length)
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
val := value.Index(i).Interface()
|
|
||||||
simpleValue, err := simpleValueCoercion(val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
|
|
||||||
}
|
|
||||||
return arrayValue.Interface(), nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("cannot convert type %T to Tree", object)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sliceToTree(object interface{}) (interface{}, error) {
|
|
||||||
// arrays are a bit tricky, since they can represent either a
|
|
||||||
// collection of simple values, which is represented by one
|
|
||||||
// *tomlValue, or an array of tables, which is represented by an
|
|
||||||
// array of *Tree.
|
|
||||||
|
|
||||||
// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice
|
|
||||||
value := reflect.ValueOf(object)
|
|
||||||
insideType := value.Type().Elem()
|
|
||||||
length := value.Len()
|
|
||||||
if length > 0 {
|
|
||||||
insideType = reflect.ValueOf(value.Index(0).Interface()).Type()
|
|
||||||
}
|
|
||||||
if insideType.Kind() == reflect.Map {
|
|
||||||
// this is considered as an array of tables
|
|
||||||
tablesArray := make([]*Tree, 0, length)
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
table := value.Index(i)
|
|
||||||
tree, err := toTree(table.Interface())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tablesArray = append(tablesArray, tree.(*Tree))
|
|
||||||
}
|
|
||||||
return tablesArray, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sliceType := typeFor(insideType.Kind())
|
|
||||||
if sliceType == nil {
|
|
||||||
sliceType = insideType
|
|
||||||
}
|
|
||||||
|
|
||||||
arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length)
|
|
||||||
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
val := value.Index(i).Interface()
|
|
||||||
simpleValue, err := simpleValueCoercion(val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
|
|
||||||
}
|
|
||||||
return &tomlValue{value: arrayValue.Interface(), position: Position{}}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toTree(object interface{}) (interface{}, error) {
|
|
||||||
value := reflect.ValueOf(object)
|
|
||||||
|
|
||||||
if value.Kind() == reflect.Map {
|
|
||||||
values := map[string]interface{}{}
|
|
||||||
keys := value.MapKeys()
|
|
||||||
for _, key := range keys {
|
|
||||||
if key.Kind() != reflect.String {
|
|
||||||
if _, ok := key.Interface().(string); !ok {
|
|
||||||
return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v := value.MapIndex(key)
|
|
||||||
newValue, err := toTree(v.Interface())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
values[key.String()] = newValue
|
|
||||||
}
|
|
||||||
return &Tree{values: values, position: Position{}}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.Kind() == reflect.Array || value.Kind() == reflect.Slice {
|
|
||||||
return sliceToTree(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
simpleValue, err := simpleValueCoercion(object)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &tomlValue{value: simpleValue, position: Position{}}, nil
|
|
||||||
}
|
|
|
@ -1,552 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type valueComplexity int
|
|
||||||
|
|
||||||
const (
|
|
||||||
valueSimple valueComplexity = iota + 1
|
|
||||||
valueComplex
|
|
||||||
)
|
|
||||||
|
|
||||||
type sortNode struct {
|
|
||||||
key string
|
|
||||||
complexity valueComplexity
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encodes a string to a TOML-compliant multi-line string value
|
|
||||||
// This function is a clone of the existing encodeTomlString function, except that whitespace characters
|
|
||||||
// are preserved. Quotation marks and backslashes are also not escaped.
|
|
||||||
func encodeMultilineTomlString(value string, commented string) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
adjacentQuoteCount := 0
|
|
||||||
|
|
||||||
b.WriteString(commented)
|
|
||||||
for i, rr := range value {
|
|
||||||
if rr != '"' {
|
|
||||||
adjacentQuoteCount = 0
|
|
||||||
} else {
|
|
||||||
adjacentQuoteCount++
|
|
||||||
}
|
|
||||||
switch rr {
|
|
||||||
case '\b':
|
|
||||||
b.WriteString(`\b`)
|
|
||||||
case '\t':
|
|
||||||
b.WriteString("\t")
|
|
||||||
case '\n':
|
|
||||||
b.WriteString("\n" + commented)
|
|
||||||
case '\f':
|
|
||||||
b.WriteString(`\f`)
|
|
||||||
case '\r':
|
|
||||||
b.WriteString("\r")
|
|
||||||
case '"':
|
|
||||||
if adjacentQuoteCount >= 3 || i == len(value)-1 {
|
|
||||||
adjacentQuoteCount = 0
|
|
||||||
b.WriteString(`\"`)
|
|
||||||
} else {
|
|
||||||
b.WriteString(`"`)
|
|
||||||
}
|
|
||||||
case '\\':
|
|
||||||
b.WriteString(`\`)
|
|
||||||
default:
|
|
||||||
intRr := uint16(rr)
|
|
||||||
if intRr < 0x001F {
|
|
||||||
b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
|
|
||||||
} else {
|
|
||||||
b.WriteRune(rr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encodes a string to a TOML-compliant string value
|
|
||||||
func encodeTomlString(value string) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
|
|
||||||
for _, rr := range value {
|
|
||||||
switch rr {
|
|
||||||
case '\b':
|
|
||||||
b.WriteString(`\b`)
|
|
||||||
case '\t':
|
|
||||||
b.WriteString(`\t`)
|
|
||||||
case '\n':
|
|
||||||
b.WriteString(`\n`)
|
|
||||||
case '\f':
|
|
||||||
b.WriteString(`\f`)
|
|
||||||
case '\r':
|
|
||||||
b.WriteString(`\r`)
|
|
||||||
case '"':
|
|
||||||
b.WriteString(`\"`)
|
|
||||||
case '\\':
|
|
||||||
b.WriteString(`\\`)
|
|
||||||
default:
|
|
||||||
intRr := uint16(rr)
|
|
||||||
if intRr < 0x001F {
|
|
||||||
b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
|
|
||||||
} else {
|
|
||||||
b.WriteRune(rr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func tomlTreeStringRepresentation(t *Tree, ord MarshalOrder) (string, error) {
|
|
||||||
var orderedVals []sortNode
|
|
||||||
switch ord {
|
|
||||||
case OrderPreserve:
|
|
||||||
orderedVals = sortByLines(t)
|
|
||||||
default:
|
|
||||||
orderedVals = sortAlphabetical(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
var values []string
|
|
||||||
for _, node := range orderedVals {
|
|
||||||
k := node.key
|
|
||||||
v := t.values[k]
|
|
||||||
|
|
||||||
repr, err := tomlValueStringRepresentation(v, "", "", ord, false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
values = append(values, quoteKeyIfNeeded(k)+" = "+repr)
|
|
||||||
}
|
|
||||||
return "{ " + strings.Join(values, ", ") + " }", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func tomlValueStringRepresentation(v interface{}, commented string, indent string, ord MarshalOrder, arraysOneElementPerLine bool) (string, error) {
|
|
||||||
// this interface check is added to dereference the change made in the writeTo function.
|
|
||||||
// That change was made to allow this function to see formatting options.
|
|
||||||
tv, ok := v.(*tomlValue)
|
|
||||||
if ok {
|
|
||||||
v = tv.value
|
|
||||||
} else {
|
|
||||||
tv = &tomlValue{}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch value := v.(type) {
|
|
||||||
case uint64:
|
|
||||||
return strconv.FormatUint(value, 10), nil
|
|
||||||
case int64:
|
|
||||||
return strconv.FormatInt(value, 10), nil
|
|
||||||
case float64:
|
|
||||||
// Default bit length is full 64
|
|
||||||
bits := 64
|
|
||||||
// Float panics if nan is used
|
|
||||||
if !math.IsNaN(value) {
|
|
||||||
// if 32 bit accuracy is enough to exactly show, use 32
|
|
||||||
_, acc := big.NewFloat(value).Float32()
|
|
||||||
if acc == big.Exact {
|
|
||||||
bits = 32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if math.Trunc(value) == value {
|
|
||||||
return strings.ToLower(strconv.FormatFloat(value, 'f', 1, bits)), nil
|
|
||||||
}
|
|
||||||
return strings.ToLower(strconv.FormatFloat(value, 'f', -1, bits)), nil
|
|
||||||
case string:
|
|
||||||
if tv.multiline {
|
|
||||||
if tv.literal {
|
|
||||||
b := strings.Builder{}
|
|
||||||
b.WriteString("'''\n")
|
|
||||||
b.Write([]byte(value))
|
|
||||||
b.WriteString("\n'''")
|
|
||||||
return b.String(), nil
|
|
||||||
} else {
|
|
||||||
return "\"\"\"\n" + encodeMultilineTomlString(value, commented) + "\"\"\"", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "\"" + encodeTomlString(value) + "\"", nil
|
|
||||||
case []byte:
|
|
||||||
b, _ := v.([]byte)
|
|
||||||
return string(b), nil
|
|
||||||
case bool:
|
|
||||||
if value {
|
|
||||||
return "true", nil
|
|
||||||
}
|
|
||||||
return "false", nil
|
|
||||||
case time.Time:
|
|
||||||
return value.Format(time.RFC3339), nil
|
|
||||||
case LocalDate:
|
|
||||||
return value.String(), nil
|
|
||||||
case LocalDateTime:
|
|
||||||
return value.String(), nil
|
|
||||||
case LocalTime:
|
|
||||||
return value.String(), nil
|
|
||||||
case *Tree:
|
|
||||||
return tomlTreeStringRepresentation(value, ord)
|
|
||||||
case nil:
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
|
|
||||||
if rv.Kind() == reflect.Slice {
|
|
||||||
var values []string
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
item := rv.Index(i).Interface()
|
|
||||||
itemRepr, err := tomlValueStringRepresentation(item, commented, indent, ord, arraysOneElementPerLine)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
values = append(values, itemRepr)
|
|
||||||
}
|
|
||||||
if arraysOneElementPerLine && len(values) > 1 {
|
|
||||||
stringBuffer := bytes.Buffer{}
|
|
||||||
valueIndent := indent + ` ` // TODO: move that to a shared encoder state
|
|
||||||
|
|
||||||
stringBuffer.WriteString("[\n")
|
|
||||||
|
|
||||||
for _, value := range values {
|
|
||||||
stringBuffer.WriteString(valueIndent)
|
|
||||||
stringBuffer.WriteString(commented + value)
|
|
||||||
stringBuffer.WriteString(`,`)
|
|
||||||
stringBuffer.WriteString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
stringBuffer.WriteString(indent + commented + "]")
|
|
||||||
|
|
||||||
return stringBuffer.String(), nil
|
|
||||||
}
|
|
||||||
return "[" + strings.Join(values, ", ") + "]", nil
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("unsupported value type %T: %v", v, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTreeArrayLine(trees []*Tree) (line int) {
|
|
||||||
// Prevent returning 0 for empty trees
|
|
||||||
line = int(^uint(0) >> 1)
|
|
||||||
// get lowest line number >= 0
|
|
||||||
for _, tv := range trees {
|
|
||||||
if tv.position.Line < line || line == 0 {
|
|
||||||
line = tv.position.Line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortByLines(t *Tree) (vals []sortNode) {
|
|
||||||
var (
|
|
||||||
line int
|
|
||||||
lines []int
|
|
||||||
tv *Tree
|
|
||||||
tom *tomlValue
|
|
||||||
node sortNode
|
|
||||||
)
|
|
||||||
vals = make([]sortNode, 0)
|
|
||||||
m := make(map[int]sortNode)
|
|
||||||
|
|
||||||
for k := range t.values {
|
|
||||||
v := t.values[k]
|
|
||||||
switch v.(type) {
|
|
||||||
case *Tree:
|
|
||||||
tv = v.(*Tree)
|
|
||||||
line = tv.position.Line
|
|
||||||
node = sortNode{key: k, complexity: valueComplex}
|
|
||||||
case []*Tree:
|
|
||||||
line = getTreeArrayLine(v.([]*Tree))
|
|
||||||
node = sortNode{key: k, complexity: valueComplex}
|
|
||||||
default:
|
|
||||||
tom = v.(*tomlValue)
|
|
||||||
line = tom.position.Line
|
|
||||||
node = sortNode{key: k, complexity: valueSimple}
|
|
||||||
}
|
|
||||||
lines = append(lines, line)
|
|
||||||
vals = append(vals, node)
|
|
||||||
m[line] = node
|
|
||||||
}
|
|
||||||
sort.Ints(lines)
|
|
||||||
|
|
||||||
for i, line := range lines {
|
|
||||||
vals[i] = m[line]
|
|
||||||
}
|
|
||||||
|
|
||||||
return vals
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortAlphabetical(t *Tree) (vals []sortNode) {
|
|
||||||
var (
|
|
||||||
node sortNode
|
|
||||||
simpVals []string
|
|
||||||
compVals []string
|
|
||||||
)
|
|
||||||
vals = make([]sortNode, 0)
|
|
||||||
m := make(map[string]sortNode)
|
|
||||||
|
|
||||||
for k := range t.values {
|
|
||||||
v := t.values[k]
|
|
||||||
switch v.(type) {
|
|
||||||
case *Tree, []*Tree:
|
|
||||||
node = sortNode{key: k, complexity: valueComplex}
|
|
||||||
compVals = append(compVals, node.key)
|
|
||||||
default:
|
|
||||||
node = sortNode{key: k, complexity: valueSimple}
|
|
||||||
simpVals = append(simpVals, node.key)
|
|
||||||
}
|
|
||||||
vals = append(vals, node)
|
|
||||||
m[node.key] = node
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simples first to match previous implementation
|
|
||||||
sort.Strings(simpVals)
|
|
||||||
i := 0
|
|
||||||
for _, key := range simpVals {
|
|
||||||
vals[i] = m[key]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(compVals)
|
|
||||||
for _, key := range compVals {
|
|
||||||
vals[i] = m[key]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return vals
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool) (int64, error) {
|
|
||||||
return t.writeToOrdered(w, indent, keyspace, bytesCount, arraysOneElementPerLine, OrderAlphabetical, " ", false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tree) writeToOrdered(w io.Writer, indent, keyspace string, bytesCount int64, arraysOneElementPerLine bool, ord MarshalOrder, indentString string, compactComments, parentCommented bool) (int64, error) {
|
|
||||||
var orderedVals []sortNode
|
|
||||||
|
|
||||||
switch ord {
|
|
||||||
case OrderPreserve:
|
|
||||||
orderedVals = sortByLines(t)
|
|
||||||
default:
|
|
||||||
orderedVals = sortAlphabetical(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range orderedVals {
|
|
||||||
switch node.complexity {
|
|
||||||
case valueComplex:
|
|
||||||
k := node.key
|
|
||||||
v := t.values[k]
|
|
||||||
|
|
||||||
combinedKey := quoteKeyIfNeeded(k)
|
|
||||||
if keyspace != "" {
|
|
||||||
combinedKey = keyspace + "." + combinedKey
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node := v.(type) {
|
|
||||||
// node has to be of those two types given how keys are sorted above
|
|
||||||
case *Tree:
|
|
||||||
tv, ok := t.values[k].(*Tree)
|
|
||||||
if !ok {
|
|
||||||
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
|
|
||||||
}
|
|
||||||
if tv.comment != "" {
|
|
||||||
comment := strings.Replace(tv.comment, "\n", "\n"+indent+"#", -1)
|
|
||||||
start := "# "
|
|
||||||
if strings.HasPrefix(comment, "#") {
|
|
||||||
start = ""
|
|
||||||
}
|
|
||||||
writtenBytesCountComment, errc := writeStrings(w, "\n", indent, start, comment)
|
|
||||||
bytesCount += int64(writtenBytesCountComment)
|
|
||||||
if errc != nil {
|
|
||||||
return bytesCount, errc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var commented string
|
|
||||||
if parentCommented || t.commented || tv.commented {
|
|
||||||
commented = "# "
|
|
||||||
}
|
|
||||||
writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[", combinedKey, "]\n")
|
|
||||||
bytesCount += int64(writtenBytesCount)
|
|
||||||
if err != nil {
|
|
||||||
return bytesCount, err
|
|
||||||
}
|
|
||||||
bytesCount, err = node.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, compactComments, parentCommented || t.commented || tv.commented)
|
|
||||||
if err != nil {
|
|
||||||
return bytesCount, err
|
|
||||||
}
|
|
||||||
case []*Tree:
|
|
||||||
for _, subTree := range node {
|
|
||||||
var commented string
|
|
||||||
if parentCommented || t.commented || subTree.commented {
|
|
||||||
commented = "# "
|
|
||||||
}
|
|
||||||
writtenBytesCount, err := writeStrings(w, "\n", indent, commented, "[[", combinedKey, "]]\n")
|
|
||||||
bytesCount += int64(writtenBytesCount)
|
|
||||||
if err != nil {
|
|
||||||
return bytesCount, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesCount, err = subTree.writeToOrdered(w, indent+indentString, combinedKey, bytesCount, arraysOneElementPerLine, ord, indentString, compactComments, parentCommented || t.commented || subTree.commented)
|
|
||||||
if err != nil {
|
|
||||||
return bytesCount, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: // Simple
|
|
||||||
k := node.key
|
|
||||||
v, ok := t.values[k].(*tomlValue)
|
|
||||||
if !ok {
|
|
||||||
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
|
|
||||||
}
|
|
||||||
|
|
||||||
var commented string
|
|
||||||
if parentCommented || t.commented || v.commented {
|
|
||||||
commented = "# "
|
|
||||||
}
|
|
||||||
repr, err := tomlValueStringRepresentation(v, commented, indent, ord, arraysOneElementPerLine)
|
|
||||||
if err != nil {
|
|
||||||
return bytesCount, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.comment != "" {
|
|
||||||
comment := strings.Replace(v.comment, "\n", "\n"+indent+"#", -1)
|
|
||||||
start := "# "
|
|
||||||
if strings.HasPrefix(comment, "#") {
|
|
||||||
start = ""
|
|
||||||
}
|
|
||||||
if !compactComments {
|
|
||||||
writtenBytesCountComment, errc := writeStrings(w, "\n")
|
|
||||||
bytesCount += int64(writtenBytesCountComment)
|
|
||||||
if errc != nil {
|
|
||||||
return bytesCount, errc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writtenBytesCountComment, errc := writeStrings(w, indent, start, comment, "\n")
|
|
||||||
bytesCount += int64(writtenBytesCountComment)
|
|
||||||
if errc != nil {
|
|
||||||
return bytesCount, errc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quotedKey := quoteKeyIfNeeded(k)
|
|
||||||
writtenBytesCount, err := writeStrings(w, indent, commented, quotedKey, " = ", repr, "\n")
|
|
||||||
bytesCount += int64(writtenBytesCount)
|
|
||||||
if err != nil {
|
|
||||||
return bytesCount, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesCount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// quote a key if it does not fit the bare key format (A-Za-z0-9_-)
|
|
||||||
// quoted keys use the same rules as strings
|
|
||||||
func quoteKeyIfNeeded(k string) string {
|
|
||||||
// when encoding a map with the 'quoteMapKeys' option enabled, the tree will contain
|
|
||||||
// keys that have already been quoted.
|
|
||||||
// not an ideal situation, but good enough of a stop gap.
|
|
||||||
if len(k) >= 2 && k[0] == '"' && k[len(k)-1] == '"' {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
isBare := true
|
|
||||||
for _, r := range k {
|
|
||||||
if !isValidBareChar(r) {
|
|
||||||
isBare = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isBare {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
return quoteKey(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
func quoteKey(k string) string {
|
|
||||||
return "\"" + encodeTomlString(k) + "\""
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeStrings(w io.Writer, s ...string) (int, error) {
|
|
||||||
var n int
|
|
||||||
for i := range s {
|
|
||||||
b, err := io.WriteString(w, s[i])
|
|
||||||
n += b
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteTo encode the Tree as Toml and writes it to the writer w.
|
|
||||||
// Returns the number of bytes written in case of success, or an error if anything happened.
|
|
||||||
func (t *Tree) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
return t.writeTo(w, "", "", 0, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToTomlString generates a human-readable representation of the current tree.
|
|
||||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser.
|
|
||||||
// If the conversion cannot be performed, ToString returns a non-nil error.
|
|
||||||
func (t *Tree) ToTomlString() (string, error) {
|
|
||||||
b, err := t.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String generates a human-readable representation of the current tree.
|
|
||||||
// Alias of ToString. Present to implement the fmt.Stringer interface.
|
|
||||||
func (t *Tree) String() string {
|
|
||||||
result, _ := t.ToTomlString()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToMap recursively generates a representation of the tree using Go built-in structures.
|
|
||||||
// The following types are used:
|
|
||||||
//
|
|
||||||
// * bool
|
|
||||||
// * float64
|
|
||||||
// * int64
|
|
||||||
// * string
|
|
||||||
// * uint64
|
|
||||||
// * time.Time
|
|
||||||
// * map[string]interface{} (where interface{} is any of this list)
|
|
||||||
// * []interface{} (where interface{} is any of this list)
|
|
||||||
func (t *Tree) ToMap() map[string]interface{} {
|
|
||||||
result := map[string]interface{}{}
|
|
||||||
|
|
||||||
for k, v := range t.values {
|
|
||||||
switch node := v.(type) {
|
|
||||||
case []*Tree:
|
|
||||||
var array []interface{}
|
|
||||||
for _, item := range node {
|
|
||||||
array = append(array, item.ToMap())
|
|
||||||
}
|
|
||||||
result[k] = array
|
|
||||||
case *Tree:
|
|
||||||
result[k] = node.ToMap()
|
|
||||||
case *tomlValue:
|
|
||||||
result[k] = tomlValueToGo(node.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func tomlValueToGo(v interface{}) interface{} {
|
|
||||||
if tree, ok := v.(*Tree); ok {
|
|
||||||
return tree.ToMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
|
|
||||||
if rv.Kind() != reflect.Slice {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
values := make([]interface{}, rv.Len())
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
item := rv.Index(i).Interface()
|
|
||||||
values[i] = tomlValueToGo(item)
|
|
||||||
}
|
|
||||||
return values
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package toml
|
|
||||||
|
|
||||||
// ValueStringRepresentation transforms an interface{} value into its toml string representation.
|
|
||||||
func ValueStringRepresentation(v interface{}, commented string, indent string, ord MarshalOrder, arraysOneElementPerLine bool) (string, error) {
|
|
||||||
return tomlValueStringRepresentation(v, commented, indent, ord, arraysOneElementPerLine)
|
|
||||||
}
|
|
|
@ -142,6 +142,11 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
|
// Dobule check that it doesn't exist.
|
||||||
|
if _, ok := m.getData()[name]; ok {
|
||||||
|
m.mu.Unlock()
|
||||||
|
return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists}
|
||||||
|
}
|
||||||
item := mem.CreateDir(name)
|
item := mem.CreateDir(name)
|
||||||
mem.SetMode(item, os.ModeDir|perm)
|
mem.SetMode(item, os.ModeDir|perm)
|
||||||
m.getData()[name] = item
|
m.getData()[name] = item
|
||||||
|
|
|
@ -16,7 +16,7 @@ endif
|
||||||
|
|
||||||
# Dependency versions
|
# Dependency versions
|
||||||
GOTESTSUM_VERSION = 1.8.0
|
GOTESTSUM_VERSION = 1.8.0
|
||||||
GOLANGCI_VERSION = 1.49.0
|
GOLANGCI_VERSION = 1.50.1
|
||||||
|
|
||||||
# Add the ability to override some variables
|
# Add the ability to override some variables
|
||||||
# Use with care
|
# Use with care
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
|
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
|
||||||
[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)
|
[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)
|
||||||
|
|
||||||
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
|
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
|
||||||
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
|
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
|
||||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square)
|
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square)
|
||||||
|
@ -40,8 +40,8 @@ go get github.com/spf13/viper
|
||||||
|
|
||||||
## What is Viper?
|
## What is Viper?
|
||||||
|
|
||||||
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed
|
Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors).
|
||||||
to work within an application, and can handle all types of configuration needs
|
It is designed to work within an application, and can handle all types of configuration needs
|
||||||
and formats. It supports:
|
and formats. It supports:
|
||||||
|
|
||||||
* setting defaults
|
* setting defaults
|
||||||
|
|
|
@ -1,39 +1,16 @@
|
||||||
//go:build viper_toml1
|
|
||||||
// +build viper_toml1
|
|
||||||
|
|
||||||
package toml
|
package toml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pelletier/go-toml"
|
"github.com/pelletier/go-toml/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
|
||||||
type Codec struct{}
|
type Codec struct{}
|
||||||
|
|
||||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||||
t, err := toml.TreeFromMap(v)
|
return toml.Marshal(v)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := t.ToTomlString()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(s), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||||
tree, err := toml.LoadBytes(b)
|
return toml.Unmarshal(b, &v)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmap := tree.ToMap()
|
|
||||||
for key, value := range tmap {
|
|
||||||
v[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
//go:build !viper_toml1
|
|
||||||
// +build !viper_toml1
|
|
||||||
|
|
||||||
package toml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pelletier/go-toml/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
|
|
||||||
type Codec struct{}
|
|
||||||
|
|
||||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|
||||||
return toml.Marshal(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
|
||||||
return toml.Unmarshal(b, &v)
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
// import "gopkg.in/yaml.v2"
|
import "gopkg.in/yaml.v3"
|
||||||
|
|
||||||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
|
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
|
||||||
type Codec struct{}
|
type Codec struct{}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
//go:build viper_yaml2
|
|
||||||
// +build viper_yaml2
|
|
||||||
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
import yamlv2 "gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
var yaml = struct {
|
|
||||||
Marshal func(in interface{}) (out []byte, err error)
|
|
||||||
Unmarshal func(in []byte, out interface{}) (err error)
|
|
||||||
}{
|
|
||||||
Marshal: yamlv2.Marshal,
|
|
||||||
Unmarshal: yamlv2.Unmarshal,
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
//go:build !viper_yaml2
|
|
||||||
// +build !viper_yaml2
|
|
||||||
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
import yamlv3 "gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
var yaml = struct {
|
|
||||||
Marshal func(in interface{}) (out []byte, err error)
|
|
||||||
Unmarshal func(in []byte, out interface{}) (err error)
|
|
||||||
}{
|
|
||||||
Marshal: yamlv3.Marshal,
|
|
||||||
Unmarshal: yamlv3.Unmarshal,
|
|
||||||
}
|
|
|
@ -421,13 +421,18 @@ var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props
|
||||||
// SupportedRemoteProviders are universally supported remote providers.
|
// SupportedRemoteProviders are universally supported remote providers.
|
||||||
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
||||||
|
|
||||||
|
// OnConfigChange sets the event handler that is called when a config file changes.
|
||||||
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
||||||
|
|
||||||
|
// OnConfigChange sets the event handler that is called when a config file changes.
|
||||||
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) {
|
||||||
v.onConfigChange = run
|
v.onConfigChange = run
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WatchConfig starts watching a config file for changes.
|
||||||
func WatchConfig() { v.WatchConfig() }
|
func WatchConfig() { v.WatchConfig() }
|
||||||
|
|
||||||
|
// WatchConfig starts watching a config file for changes.
|
||||||
func (v *Viper) WatchConfig() {
|
func (v *Viper) WatchConfig() {
|
||||||
initWG := sync.WaitGroup{}
|
initWG := sync.WaitGroup{}
|
||||||
initWG.Add(1)
|
initWG.Add(1)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package gotenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -174,9 +175,36 @@ func Write(env Env, filename string) error {
|
||||||
return file.Sync()
|
return file.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// splitLines is a valid SplitFunc for a bufio.Scanner. It will split lines on CR ('\r'), LF ('\n') or CRLF (any of the three sequences).
|
||||||
|
// If a CR is immediately followed by a LF, it is treated as a CRLF (one single line break).
|
||||||
|
func splitLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
|
if atEOF && len(data) == 0 {
|
||||||
|
return 0, nil, bufio.ErrFinalToken
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := bytes.IndexAny(data, "\r\n")
|
||||||
|
switch {
|
||||||
|
case atEOF && idx < 0:
|
||||||
|
return len(data), data, bufio.ErrFinalToken
|
||||||
|
|
||||||
|
case idx < 0:
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume CR or LF
|
||||||
|
eol := idx + 1
|
||||||
|
// detect CRLF
|
||||||
|
if len(data) > eol && data[eol-1] == '\r' && data[eol] == '\n' {
|
||||||
|
eol++
|
||||||
|
}
|
||||||
|
|
||||||
|
return eol, data[:idx], nil
|
||||||
|
}
|
||||||
|
|
||||||
func strictParse(r io.Reader, override bool) (Env, error) {
|
func strictParse(r io.Reader, override bool) (Env, error) {
|
||||||
env := make(Env)
|
env := make(Env)
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
|
scanner.Split(splitLines)
|
||||||
|
|
||||||
firstLine := true
|
firstLine := true
|
||||||
|
|
||||||
|
@ -283,7 +311,6 @@ func parseLine(s string, env Env, override bool) error {
|
||||||
return varReplacement(s, hsq, env, override)
|
return varReplacement(s, hsq, env, override)
|
||||||
}
|
}
|
||||||
val = varRgx.ReplaceAllStringFunc(val, fv)
|
val = varRgx.ReplaceAllStringFunc(val, fv)
|
||||||
val = parseVal(val, env, hdq, override)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
env[key] = val
|
env[key] = val
|
||||||
|
@ -352,18 +379,3 @@ func checkFormat(s string, env Env) error {
|
||||||
|
|
||||||
return fmt.Errorf("line `%s` doesn't match format", s)
|
return fmt.Errorf("line `%s` doesn't match format", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseVal(val string, env Env, ignoreNewlines bool, override bool) string {
|
|
||||||
if strings.Contains(val, "=") && !ignoreNewlines {
|
|
||||||
kv := strings.Split(val, "\r")
|
|
||||||
|
|
||||||
if len(kv) > 1 {
|
|
||||||
val = kv[0]
|
|
||||||
for _, l := range kv[1:] {
|
|
||||||
_ = parseLine(l, env, override)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
|
@ -313,8 +313,8 @@ github.com/klauspost/cpuid/v2
|
||||||
# github.com/leodido/go-urn v1.2.1
|
# github.com/leodido/go-urn v1.2.1
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/leodido/go-urn
|
github.com/leodido/go-urn
|
||||||
# github.com/magiconair/properties v1.8.6
|
# github.com/magiconair/properties v1.8.7
|
||||||
## explicit; go 1.13
|
## explicit; go 1.19
|
||||||
github.com/magiconair/properties
|
github.com/magiconair/properties
|
||||||
# github.com/mattn/go-isatty v0.0.17
|
# github.com/mattn/go-isatty v0.0.17
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
|
@ -360,9 +360,6 @@ github.com/oklog/ulid
|
||||||
# github.com/opencontainers/runtime-spec v1.0.2
|
# github.com/opencontainers/runtime-spec v1.0.2
|
||||||
## explicit
|
## explicit
|
||||||
github.com/opencontainers/runtime-spec/specs-go
|
github.com/opencontainers/runtime-spec/specs-go
|
||||||
# github.com/pelletier/go-toml v1.9.5
|
|
||||||
## explicit; go 1.12
|
|
||||||
github.com/pelletier/go-toml
|
|
||||||
# github.com/pelletier/go-toml/v2 v2.0.6
|
# github.com/pelletier/go-toml/v2 v2.0.6
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/pelletier/go-toml/v2
|
github.com/pelletier/go-toml/v2
|
||||||
|
@ -391,7 +388,7 @@ github.com/rs/xid
|
||||||
# github.com/sirupsen/logrus v1.9.0
|
# github.com/sirupsen/logrus v1.9.0
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/sirupsen/logrus
|
github.com/sirupsen/logrus
|
||||||
# github.com/spf13/afero v1.9.2
|
# github.com/spf13/afero v1.9.3
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/spf13/afero
|
github.com/spf13/afero
|
||||||
github.com/spf13/afero/internal/common
|
github.com/spf13/afero/internal/common
|
||||||
|
@ -408,7 +405,7 @@ github.com/spf13/jwalterweatherman
|
||||||
# github.com/spf13/pflag v1.0.5
|
# github.com/spf13/pflag v1.0.5
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/spf13/pflag
|
github.com/spf13/pflag
|
||||||
# github.com/spf13/viper v1.14.0
|
# github.com/spf13/viper v1.15.0
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/spf13/viper
|
github.com/spf13/viper
|
||||||
github.com/spf13/viper/internal/encoding
|
github.com/spf13/viper/internal/encoding
|
||||||
|
@ -424,7 +421,7 @@ github.com/spf13/viper/internal/encoding/yaml
|
||||||
github.com/stretchr/testify/assert
|
github.com/stretchr/testify/assert
|
||||||
github.com/stretchr/testify/require
|
github.com/stretchr/testify/require
|
||||||
github.com/stretchr/testify/suite
|
github.com/stretchr/testify/suite
|
||||||
# github.com/subosito/gotenv v1.4.1
|
# github.com/subosito/gotenv v1.4.2
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/subosito/gotenv
|
github.com/subosito/gotenv
|
||||||
# github.com/superseriousbusiness/activity v1.2.1-gts
|
# github.com/superseriousbusiness/activity v1.2.1-gts
|
||||||
|
|
Loading…
Reference in New Issue