Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
songlinfeng
container-toolkit
Commits
d7e13eb9
Commit
d7e13eb9
authored
Oct 22, 2025
by
songlinfeng
Browse files
add dtk-container-toolkit
parent
fcdba4f3
Changes
345
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
5151 additions
and
0 deletions
+5151
-0
vendor/github.com/pelletier/go-toml/azure-pipelines.yml
vendor/github.com/pelletier/go-toml/azure-pipelines.yml
+188
-0
vendor/github.com/pelletier/go-toml/benchmark.sh
vendor/github.com/pelletier/go-toml/benchmark.sh
+35
-0
vendor/github.com/pelletier/go-toml/doc.go
vendor/github.com/pelletier/go-toml/doc.go
+23
-0
vendor/github.com/pelletier/go-toml/example-crlf.toml
vendor/github.com/pelletier/go-toml/example-crlf.toml
+30
-0
vendor/github.com/pelletier/go-toml/example.toml
vendor/github.com/pelletier/go-toml/example.toml
+30
-0
vendor/github.com/pelletier/go-toml/fuzz.go
vendor/github.com/pelletier/go-toml/fuzz.go
+31
-0
vendor/github.com/pelletier/go-toml/fuzz.sh
vendor/github.com/pelletier/go-toml/fuzz.sh
+15
-0
vendor/github.com/pelletier/go-toml/keysparsing.go
vendor/github.com/pelletier/go-toml/keysparsing.go
+112
-0
vendor/github.com/pelletier/go-toml/lexer.go
vendor/github.com/pelletier/go-toml/lexer.go
+1031
-0
vendor/github.com/pelletier/go-toml/localtime.go
vendor/github.com/pelletier/go-toml/localtime.go
+287
-0
vendor/github.com/pelletier/go-toml/marshal.go
vendor/github.com/pelletier/go-toml/marshal.go
+1308
-0
vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
...hub.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
+39
-0
vendor/github.com/pelletier/go-toml/marshal_test.toml
vendor/github.com/pelletier/go-toml/marshal_test.toml
+39
-0
vendor/github.com/pelletier/go-toml/parser.go
vendor/github.com/pelletier/go-toml/parser.go
+507
-0
vendor/github.com/pelletier/go-toml/position.go
vendor/github.com/pelletier/go-toml/position.go
+29
-0
vendor/github.com/pelletier/go-toml/token.go
vendor/github.com/pelletier/go-toml/token.go
+136
-0
vendor/github.com/pelletier/go-toml/toml.go
vendor/github.com/pelletier/go-toml/toml.go
+533
-0
vendor/github.com/pelletier/go-toml/tomlpub.go
vendor/github.com/pelletier/go-toml/tomlpub.go
+71
-0
vendor/github.com/pelletier/go-toml/tomltree_create.go
vendor/github.com/pelletier/go-toml/tomltree_create.go
+155
-0
vendor/github.com/pelletier/go-toml/tomltree_write.go
vendor/github.com/pelletier/go-toml/tomltree_write.go
+552
-0
No files found.
Too many changes to show.
To preserve performance only
345 of 345+
files are displayed.
Plain diff
Email patch
vendor/github.com/pelletier/go-toml/azure-pipelines.yml
0 → 100644
View file @
d7e13eb9
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'
vendor/github.com/pelletier/go-toml/benchmark.sh
0 → 100644
View file @
d7e13eb9
#!/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
}
vendor/github.com/pelletier/go-toml/doc.go
0 → 100644
View file @
d7e13eb9
// 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
vendor/github.com/pelletier/go-toml/example-crlf.toml
0 → 100644
View file @
d7e13eb9
# This is a TOML document. Boom.
title
=
"TOML Example"
[owner]
name
=
"Tom Preston-Werner"
organization
=
"GitHub"
bio
=
"GitHub Cofounder & CEO
\n
Likes 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
\ No newline at end of file
vendor/github.com/pelletier/go-toml/example.toml
0 → 100644
View file @
d7e13eb9
# This is a TOML document. Boom.
title
=
"TOML Example"
[owner]
name
=
"Tom Preston-Werner"
organization
=
"GitHub"
bio
=
"GitHub Cofounder & CEO
\n
Likes 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
\ No newline at end of file
vendor/github.com/pelletier/go-toml/fuzz.go
0 → 100644
View file @
d7e13eb9
// +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
}
vendor/github.com/pelletier/go-toml/fuzz.sh
0 → 100644
View file @
d7e13eb9
#! /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
vendor/github.com/pelletier/go-toml/keysparsing.go
0 → 100644
View file @
d7e13eb9
// 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
)
}
vendor/github.com/pelletier/go-toml/lexer.go
0 → 100644
View file @
d7e13eb9
// TOML lexer.
//
// Written using the principles developed by Rob Pike in
// http://www.youtube.com/watch?v=HxaD_trXwRE
package
toml
import
(
"bytes"
"errors"
"fmt"
"strconv"
"strings"
)
// Define state functions
type
tomlLexStateFn
func
()
tomlLexStateFn
// Define lexer
type
tomlLexer
struct
{
inputIdx
int
input
[]
rune
// Textual source
currentTokenStart
int
currentTokenStop
int
tokens
[]
token
brackets
[]
rune
line
int
col
int
endbufferLine
int
endbufferCol
int
}
// Basic read operations on input
func
(
l
*
tomlLexer
)
read
()
rune
{
r
:=
l
.
peek
()
if
r
==
'\n'
{
l
.
endbufferLine
++
l
.
endbufferCol
=
1
}
else
{
l
.
endbufferCol
++
}
l
.
inputIdx
++
return
r
}
func
(
l
*
tomlLexer
)
next
()
rune
{
r
:=
l
.
read
()
if
r
!=
eof
{
l
.
currentTokenStop
++
}
return
r
}
func
(
l
*
tomlLexer
)
ignore
()
{
l
.
currentTokenStart
=
l
.
currentTokenStop
l
.
line
=
l
.
endbufferLine
l
.
col
=
l
.
endbufferCol
}
func
(
l
*
tomlLexer
)
skip
()
{
l
.
next
()
l
.
ignore
()
}
func
(
l
*
tomlLexer
)
fastForward
(
n
int
)
{
for
i
:=
0
;
i
<
n
;
i
++
{
l
.
next
()
}
}
func
(
l
*
tomlLexer
)
emitWithValue
(
t
tokenType
,
value
string
)
{
l
.
tokens
=
append
(
l
.
tokens
,
token
{
Position
:
Position
{
l
.
line
,
l
.
col
},
typ
:
t
,
val
:
value
,
})
l
.
ignore
()
}
func
(
l
*
tomlLexer
)
emit
(
t
tokenType
)
{
l
.
emitWithValue
(
t
,
string
(
l
.
input
[
l
.
currentTokenStart
:
l
.
currentTokenStop
]))
}
func
(
l
*
tomlLexer
)
peek
()
rune
{
if
l
.
inputIdx
>=
len
(
l
.
input
)
{
return
eof
}
return
l
.
input
[
l
.
inputIdx
]
}
func
(
l
*
tomlLexer
)
peekString
(
size
int
)
string
{
maxIdx
:=
len
(
l
.
input
)
upperIdx
:=
l
.
inputIdx
+
size
// FIXME: potential overflow
if
upperIdx
>
maxIdx
{
upperIdx
=
maxIdx
}
return
string
(
l
.
input
[
l
.
inputIdx
:
upperIdx
])
}
func
(
l
*
tomlLexer
)
follow
(
next
string
)
bool
{
return
next
==
l
.
peekString
(
len
(
next
))
}
// Error management
func
(
l
*
tomlLexer
)
errorf
(
format
string
,
args
...
interface
{})
tomlLexStateFn
{
l
.
tokens
=
append
(
l
.
tokens
,
token
{
Position
:
Position
{
l
.
line
,
l
.
col
},
typ
:
tokenError
,
val
:
fmt
.
Sprintf
(
format
,
args
...
),
})
return
nil
}
// State functions
func
(
l
*
tomlLexer
)
lexVoid
()
tomlLexStateFn
{
for
{
next
:=
l
.
peek
()
switch
next
{
case
'}'
:
// after '{'
return
l
.
lexRightCurlyBrace
case
'['
:
return
l
.
lexTableKey
case
'#'
:
return
l
.
lexComment
(
l
.
lexVoid
)
case
'='
:
return
l
.
lexEqual
case
'\r'
:
fallthrough
case
'\n'
:
l
.
skip
()
continue
}
if
isSpace
(
next
)
{
l
.
skip
()
}
if
isKeyStartChar
(
next
)
{
return
l
.
lexKey
}
if
next
==
eof
{
l
.
next
()
break
}
}
l
.
emit
(
tokenEOF
)
return
nil
}
func
(
l
*
tomlLexer
)
lexRvalue
()
tomlLexStateFn
{
for
{
next
:=
l
.
peek
()
switch
next
{
case
'.'
:
return
l
.
errorf
(
"cannot start float with a dot"
)
case
'='
:
return
l
.
lexEqual
case
'['
:
return
l
.
lexLeftBracket
case
']'
:
return
l
.
lexRightBracket
case
'{'
:
return
l
.
lexLeftCurlyBrace
case
'}'
:
return
l
.
lexRightCurlyBrace
case
'#'
:
return
l
.
lexComment
(
l
.
lexRvalue
)
case
'"'
:
return
l
.
lexString
case
'\'
'
:
return
l
.
lexLiteralString
case
','
:
return
l
.
lexComma
case
'\r'
:
fallthrough
case
'\n'
:
l
.
skip
()
if
len
(
l
.
brackets
)
>
0
&&
l
.
brackets
[
len
(
l
.
brackets
)
-
1
]
==
'['
{
return
l
.
lexRvalue
}
return
l
.
lexVoid
}
if
l
.
follow
(
"true"
)
{
return
l
.
lexTrue
}
if
l
.
follow
(
"false"
)
{
return
l
.
lexFalse
}
if
l
.
follow
(
"inf"
)
{
return
l
.
lexInf
}
if
l
.
follow
(
"nan"
)
{
return
l
.
lexNan
}
if
isSpace
(
next
)
{
l
.
skip
()
continue
}
if
next
==
eof
{
l
.
next
()
break
}
if
next
==
'+'
||
next
==
'-'
{
return
l
.
lexNumber
}
if
isDigit
(
next
)
{
return
l
.
lexDateTimeOrNumber
}
return
l
.
errorf
(
"no value can start with %c"
,
next
)
}
l
.
emit
(
tokenEOF
)
return
nil
}
func
(
l
*
tomlLexer
)
lexDateTimeOrNumber
()
tomlLexStateFn
{
// Could be either a date/time, or a digit.
// The options for date/times are:
// YYYY-... => date or date-time
// HH:... => time
// Anything else should be a number.
lookAhead
:=
l
.
peekString
(
5
)
if
len
(
lookAhead
)
<
3
{
return
l
.
lexNumber
()
}
for
idx
,
r
:=
range
lookAhead
{
if
!
isDigit
(
r
)
{
if
idx
==
2
&&
r
==
':'
{
return
l
.
lexDateTimeOrTime
()
}
if
idx
==
4
&&
r
==
'-'
{
return
l
.
lexDateTimeOrTime
()
}
return
l
.
lexNumber
()
}
}
return
l
.
lexNumber
()
}
func
(
l
*
tomlLexer
)
lexLeftCurlyBrace
()
tomlLexStateFn
{
l
.
next
()
l
.
emit
(
tokenLeftCurlyBrace
)
l
.
brackets
=
append
(
l
.
brackets
,
'{'
)
return
l
.
lexVoid
}
func
(
l
*
tomlLexer
)
lexRightCurlyBrace
()
tomlLexStateFn
{
l
.
next
()
l
.
emit
(
tokenRightCurlyBrace
)
if
len
(
l
.
brackets
)
==
0
||
l
.
brackets
[
len
(
l
.
brackets
)
-
1
]
!=
'{'
{
return
l
.
errorf
(
"cannot have '}' here"
)
}
l
.
brackets
=
l
.
brackets
[
:
len
(
l
.
brackets
)
-
1
]
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexDateTimeOrTime
()
tomlLexStateFn
{
// Example matches:
// 1979-05-27T07:32:00Z
// 1979-05-27T00:32:00-07:00
// 1979-05-27T00:32:00.999999-07:00
// 1979-05-27 07:32:00Z
// 1979-05-27 00:32:00-07:00
// 1979-05-27 00:32:00.999999-07:00
// 1979-05-27T07:32:00
// 1979-05-27T00:32:00.999999
// 1979-05-27 07:32:00
// 1979-05-27 00:32:00.999999
// 1979-05-27
// 07:32:00
// 00:32:00.999999
// we already know those two are digits
l
.
next
()
l
.
next
()
// Got 2 digits. At that point it could be either a time or a date(-time).
r
:=
l
.
next
()
if
r
==
':'
{
return
l
.
lexTime
()
}
return
l
.
lexDateTime
()
}
func
(
l
*
tomlLexer
)
lexDateTime
()
tomlLexStateFn
{
// This state accepts an offset date-time, a local date-time, or a local date.
//
// v--- cursor
// 1979-05-27T07:32:00Z
// 1979-05-27T00:32:00-07:00
// 1979-05-27T00:32:00.999999-07:00
// 1979-05-27 07:32:00Z
// 1979-05-27 00:32:00-07:00
// 1979-05-27 00:32:00.999999-07:00
// 1979-05-27T07:32:00
// 1979-05-27T00:32:00.999999
// 1979-05-27 07:32:00
// 1979-05-27 00:32:00.999999
// 1979-05-27
// date
// already checked by lexRvalue
l
.
next
()
// digit
l
.
next
()
// -
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid month digit in date: %c"
,
r
)
}
}
r
:=
l
.
next
()
if
r
!=
'-'
{
return
l
.
errorf
(
"expected - to separate month of a date, not %c"
,
r
)
}
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid day digit in date: %c"
,
r
)
}
}
l
.
emit
(
tokenLocalDate
)
r
=
l
.
peek
()
if
r
==
eof
{
return
l
.
lexRvalue
}
if
r
!=
' '
&&
r
!=
'T'
{
return
l
.
errorf
(
"incorrect date/time separation character: %c"
,
r
)
}
if
r
==
' '
{
lookAhead
:=
l
.
peekString
(
3
)[
1
:
]
if
len
(
lookAhead
)
<
2
{
return
l
.
lexRvalue
}
for
_
,
r
:=
range
lookAhead
{
if
!
isDigit
(
r
)
{
return
l
.
lexRvalue
}
}
}
l
.
skip
()
// skip the T or ' '
// time
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid hour digit in time: %c"
,
r
)
}
}
r
=
l
.
next
()
if
r
!=
':'
{
return
l
.
errorf
(
"time hour/minute separator should be :, not %c"
,
r
)
}
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid minute digit in time: %c"
,
r
)
}
}
r
=
l
.
next
()
if
r
!=
':'
{
return
l
.
errorf
(
"time minute/second separator should be :, not %c"
,
r
)
}
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid second digit in time: %c"
,
r
)
}
}
r
=
l
.
peek
()
if
r
==
'.'
{
l
.
next
()
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"expected at least one digit in time's fraction, not %c"
,
r
)
}
for
{
r
:=
l
.
peek
()
if
!
isDigit
(
r
)
{
break
}
l
.
next
()
}
}
l
.
emit
(
tokenLocalTime
)
return
l
.
lexTimeOffset
}
func
(
l
*
tomlLexer
)
lexTimeOffset
()
tomlLexStateFn
{
// potential offset
// Z
// -07:00
// +07:00
// nothing
r
:=
l
.
peek
()
if
r
==
'Z'
{
l
.
next
()
l
.
emit
(
tokenTimeOffset
)
}
else
if
r
==
'+'
||
r
==
'-'
{
l
.
next
()
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid hour digit in time offset: %c"
,
r
)
}
}
r
=
l
.
next
()
if
r
!=
':'
{
return
l
.
errorf
(
"time offset hour/minute separator should be :, not %c"
,
r
)
}
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid minute digit in time offset: %c"
,
r
)
}
}
l
.
emit
(
tokenTimeOffset
)
}
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexTime
()
tomlLexStateFn
{
// v--- cursor
// 07:32:00
// 00:32:00.999999
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid minute digit in time: %c"
,
r
)
}
}
r
:=
l
.
next
()
if
r
!=
':'
{
return
l
.
errorf
(
"time minute/second separator should be :, not %c"
,
r
)
}
for
i
:=
0
;
i
<
2
;
i
++
{
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"invalid second digit in time: %c"
,
r
)
}
}
r
=
l
.
peek
()
if
r
==
'.'
{
l
.
next
()
r
:=
l
.
next
()
if
!
isDigit
(
r
)
{
return
l
.
errorf
(
"expected at least one digit in time's fraction, not %c"
,
r
)
}
for
{
r
:=
l
.
peek
()
if
!
isDigit
(
r
)
{
break
}
l
.
next
()
}
}
l
.
emit
(
tokenLocalTime
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexTrue
()
tomlLexStateFn
{
l
.
fastForward
(
4
)
l
.
emit
(
tokenTrue
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexFalse
()
tomlLexStateFn
{
l
.
fastForward
(
5
)
l
.
emit
(
tokenFalse
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexInf
()
tomlLexStateFn
{
l
.
fastForward
(
3
)
l
.
emit
(
tokenInf
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexNan
()
tomlLexStateFn
{
l
.
fastForward
(
3
)
l
.
emit
(
tokenNan
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexEqual
()
tomlLexStateFn
{
l
.
next
()
l
.
emit
(
tokenEqual
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexComma
()
tomlLexStateFn
{
l
.
next
()
l
.
emit
(
tokenComma
)
if
len
(
l
.
brackets
)
>
0
&&
l
.
brackets
[
len
(
l
.
brackets
)
-
1
]
==
'{'
{
return
l
.
lexVoid
}
return
l
.
lexRvalue
}
// Parse the key and emits its value without escape sequences.
// bare keys, basic string keys and literal string keys are supported.
func
(
l
*
tomlLexer
)
lexKey
()
tomlLexStateFn
{
var
sb
strings
.
Builder
for
r
:=
l
.
peek
();
isKeyChar
(
r
)
||
r
==
'\n'
||
r
==
'\r'
;
r
=
l
.
peek
()
{
if
r
==
'"'
{
l
.
next
()
str
,
err
:=
l
.
lexStringAsString
(
`"`
,
false
,
true
)
if
err
!=
nil
{
return
l
.
errorf
(
err
.
Error
())
}
sb
.
WriteString
(
"
\"
"
)
sb
.
WriteString
(
str
)
sb
.
WriteString
(
"
\"
"
)
l
.
next
()
continue
}
else
if
r
==
'\'
'
{
l
.
next
()
str
,
err
:=
l
.
lexLiteralStringAsString
(
`'`
,
false
)
if
err
!=
nil
{
return
l
.
errorf
(
err
.
Error
())
}
sb
.
WriteString
(
"'"
)
sb
.
WriteString
(
str
)
sb
.
WriteString
(
"'"
)
l
.
next
()
continue
}
else
if
r
==
'\n'
{
return
l
.
errorf
(
"keys cannot contain new lines"
)
}
else
if
isSpace
(
r
)
{
var
str
strings
.
Builder
str
.
WriteString
(
" "
)
// skip trailing whitespace
l
.
next
()
for
r
=
l
.
peek
();
isSpace
(
r
);
r
=
l
.
peek
()
{
str
.
WriteRune
(
r
)
l
.
next
()
}
// break loop if not a dot
if
r
!=
'.'
{
break
}
str
.
WriteString
(
"."
)
// skip trailing whitespace after dot
l
.
next
()
for
r
=
l
.
peek
();
isSpace
(
r
);
r
=
l
.
peek
()
{
str
.
WriteRune
(
r
)
l
.
next
()
}
sb
.
WriteString
(
str
.
String
())
continue
}
else
if
r
==
'.'
{
// skip
}
else
if
!
isValidBareChar
(
r
)
{
return
l
.
errorf
(
"keys cannot contain %c character"
,
r
)
}
sb
.
WriteRune
(
r
)
l
.
next
()
}
l
.
emitWithValue
(
tokenKey
,
sb
.
String
())
return
l
.
lexVoid
}
func
(
l
*
tomlLexer
)
lexComment
(
previousState
tomlLexStateFn
)
tomlLexStateFn
{
return
func
()
tomlLexStateFn
{
for
next
:=
l
.
peek
();
next
!=
'\n'
&&
next
!=
eof
;
next
=
l
.
peek
()
{
if
next
==
'\r'
&&
l
.
follow
(
"
\r\n
"
)
{
break
}
l
.
next
()
}
l
.
ignore
()
return
previousState
}
}
func
(
l
*
tomlLexer
)
lexLeftBracket
()
tomlLexStateFn
{
l
.
next
()
l
.
emit
(
tokenLeftBracket
)
l
.
brackets
=
append
(
l
.
brackets
,
'['
)
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexLiteralStringAsString
(
terminator
string
,
discardLeadingNewLine
bool
)
(
string
,
error
)
{
var
sb
strings
.
Builder
if
discardLeadingNewLine
{
if
l
.
follow
(
"
\r\n
"
)
{
l
.
skip
()
l
.
skip
()
}
else
if
l
.
peek
()
==
'\n'
{
l
.
skip
()
}
}
// find end of string
for
{
if
l
.
follow
(
terminator
)
{
return
sb
.
String
(),
nil
}
next
:=
l
.
peek
()
if
next
==
eof
{
break
}
sb
.
WriteRune
(
l
.
next
())
}
return
""
,
errors
.
New
(
"unclosed string"
)
}
func
(
l
*
tomlLexer
)
lexLiteralString
()
tomlLexStateFn
{
l
.
skip
()
// handle special case for triple-quote
terminator
:=
"'"
discardLeadingNewLine
:=
false
if
l
.
follow
(
"''"
)
{
l
.
skip
()
l
.
skip
()
terminator
=
"'''"
discardLeadingNewLine
=
true
}
str
,
err
:=
l
.
lexLiteralStringAsString
(
terminator
,
discardLeadingNewLine
)
if
err
!=
nil
{
return
l
.
errorf
(
err
.
Error
())
}
l
.
emitWithValue
(
tokenString
,
str
)
l
.
fastForward
(
len
(
terminator
))
l
.
ignore
()
return
l
.
lexRvalue
}
// Lex a string and return the results as a string.
// Terminator is the substring indicating the end of the token.
// The resulting string does not include the terminator.
func
(
l
*
tomlLexer
)
lexStringAsString
(
terminator
string
,
discardLeadingNewLine
,
acceptNewLines
bool
)
(
string
,
error
)
{
var
sb
strings
.
Builder
if
discardLeadingNewLine
{
if
l
.
follow
(
"
\r\n
"
)
{
l
.
skip
()
l
.
skip
()
}
else
if
l
.
peek
()
==
'\n'
{
l
.
skip
()
}
}
for
{
if
l
.
follow
(
terminator
)
{
return
sb
.
String
(),
nil
}
if
l
.
follow
(
"
\\
"
)
{
l
.
next
()
switch
l
.
peek
()
{
case
'\r'
:
fallthrough
case
'\n'
:
fallthrough
case
'\t'
:
fallthrough
case
' '
:
// skip all whitespace chars following backslash
for
strings
.
ContainsRune
(
"
\r\n\t
"
,
l
.
peek
())
{
l
.
next
()
}
case
'"'
:
sb
.
WriteString
(
"
\"
"
)
l
.
next
()
case
'n'
:
sb
.
WriteString
(
"
\n
"
)
l
.
next
()
case
'b'
:
sb
.
WriteString
(
"
\b
"
)
l
.
next
()
case
'f'
:
sb
.
WriteString
(
"
\f
"
)
l
.
next
()
case
'/'
:
sb
.
WriteString
(
"/"
)
l
.
next
()
case
't'
:
sb
.
WriteString
(
"
\t
"
)
l
.
next
()
case
'r'
:
sb
.
WriteString
(
"
\r
"
)
l
.
next
()
case
'\\'
:
sb
.
WriteString
(
"
\\
"
)
l
.
next
()
case
'u'
:
l
.
next
()
var
code
strings
.
Builder
for
i
:=
0
;
i
<
4
;
i
++
{
c
:=
l
.
peek
()
if
!
isHexDigit
(
c
)
{
return
""
,
errors
.
New
(
"unfinished unicode escape"
)
}
l
.
next
()
code
.
WriteRune
(
c
)
}
intcode
,
err
:=
strconv
.
ParseInt
(
code
.
String
(),
16
,
32
)
if
err
!=
nil
{
return
""
,
errors
.
New
(
"invalid unicode escape:
\\
u"
+
code
.
String
())
}
sb
.
WriteRune
(
rune
(
intcode
))
case
'U'
:
l
.
next
()
var
code
strings
.
Builder
for
i
:=
0
;
i
<
8
;
i
++
{
c
:=
l
.
peek
()
if
!
isHexDigit
(
c
)
{
return
""
,
errors
.
New
(
"unfinished unicode escape"
)
}
l
.
next
()
code
.
WriteRune
(
c
)
}
intcode
,
err
:=
strconv
.
ParseInt
(
code
.
String
(),
16
,
64
)
if
err
!=
nil
{
return
""
,
errors
.
New
(
"invalid unicode escape:
\\
U"
+
code
.
String
())
}
sb
.
WriteRune
(
rune
(
intcode
))
default
:
return
""
,
errors
.
New
(
"invalid escape sequence:
\\
"
+
string
(
l
.
peek
()))
}
}
else
{
r
:=
l
.
peek
()
if
0x00
<=
r
&&
r
<=
0x1F
&&
r
!=
'\t'
&&
!
(
acceptNewLines
&&
(
r
==
'\n'
||
r
==
'\r'
))
{
return
""
,
fmt
.
Errorf
(
"unescaped control character %U"
,
r
)
}
l
.
next
()
sb
.
WriteRune
(
r
)
}
if
l
.
peek
()
==
eof
{
break
}
}
return
""
,
errors
.
New
(
"unclosed string"
)
}
func
(
l
*
tomlLexer
)
lexString
()
tomlLexStateFn
{
l
.
skip
()
// handle special case for triple-quote
terminator
:=
`"`
discardLeadingNewLine
:=
false
acceptNewLines
:=
false
if
l
.
follow
(
`""`
)
{
l
.
skip
()
l
.
skip
()
terminator
=
`"""`
discardLeadingNewLine
=
true
acceptNewLines
=
true
}
str
,
err
:=
l
.
lexStringAsString
(
terminator
,
discardLeadingNewLine
,
acceptNewLines
)
if
err
!=
nil
{
return
l
.
errorf
(
err
.
Error
())
}
l
.
emitWithValue
(
tokenString
,
str
)
l
.
fastForward
(
len
(
terminator
))
l
.
ignore
()
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
lexTableKey
()
tomlLexStateFn
{
l
.
next
()
if
l
.
peek
()
==
'['
{
// token '[[' signifies an array of tables
l
.
next
()
l
.
emit
(
tokenDoubleLeftBracket
)
return
l
.
lexInsideTableArrayKey
}
// vanilla table key
l
.
emit
(
tokenLeftBracket
)
return
l
.
lexInsideTableKey
}
// Parse the key till "]]", but only bare keys are supported
func
(
l
*
tomlLexer
)
lexInsideTableArrayKey
()
tomlLexStateFn
{
for
r
:=
l
.
peek
();
r
!=
eof
;
r
=
l
.
peek
()
{
switch
r
{
case
']'
:
if
l
.
currentTokenStop
>
l
.
currentTokenStart
{
l
.
emit
(
tokenKeyGroupArray
)
}
l
.
next
()
if
l
.
peek
()
!=
']'
{
break
}
l
.
next
()
l
.
emit
(
tokenDoubleRightBracket
)
return
l
.
lexVoid
case
'['
:
return
l
.
errorf
(
"table array key cannot contain ']'"
)
default
:
l
.
next
()
}
}
return
l
.
errorf
(
"unclosed table array key"
)
}
// Parse the key till "]" but only bare keys are supported
func
(
l
*
tomlLexer
)
lexInsideTableKey
()
tomlLexStateFn
{
for
r
:=
l
.
peek
();
r
!=
eof
;
r
=
l
.
peek
()
{
switch
r
{
case
']'
:
if
l
.
currentTokenStop
>
l
.
currentTokenStart
{
l
.
emit
(
tokenKeyGroup
)
}
l
.
next
()
l
.
emit
(
tokenRightBracket
)
return
l
.
lexVoid
case
'['
:
return
l
.
errorf
(
"table key cannot contain ']'"
)
default
:
l
.
next
()
}
}
return
l
.
errorf
(
"unclosed table key"
)
}
func
(
l
*
tomlLexer
)
lexRightBracket
()
tomlLexStateFn
{
l
.
next
()
l
.
emit
(
tokenRightBracket
)
if
len
(
l
.
brackets
)
==
0
||
l
.
brackets
[
len
(
l
.
brackets
)
-
1
]
!=
'['
{
return
l
.
errorf
(
"cannot have ']' here"
)
}
l
.
brackets
=
l
.
brackets
[
:
len
(
l
.
brackets
)
-
1
]
return
l
.
lexRvalue
}
type
validRuneFn
func
(
r
rune
)
bool
func
isValidHexRune
(
r
rune
)
bool
{
return
r
>=
'a'
&&
r
<=
'f'
||
r
>=
'A'
&&
r
<=
'F'
||
r
>=
'0'
&&
r
<=
'9'
||
r
==
'_'
}
func
isValidOctalRune
(
r
rune
)
bool
{
return
r
>=
'0'
&&
r
<=
'7'
||
r
==
'_'
}
func
isValidBinaryRune
(
r
rune
)
bool
{
return
r
==
'0'
||
r
==
'1'
||
r
==
'_'
}
func
(
l
*
tomlLexer
)
lexNumber
()
tomlLexStateFn
{
r
:=
l
.
peek
()
if
r
==
'0'
{
follow
:=
l
.
peekString
(
2
)
if
len
(
follow
)
==
2
{
var
isValidRune
validRuneFn
switch
follow
[
1
]
{
case
'x'
:
isValidRune
=
isValidHexRune
case
'o'
:
isValidRune
=
isValidOctalRune
case
'b'
:
isValidRune
=
isValidBinaryRune
default
:
if
follow
[
1
]
>=
'a'
&&
follow
[
1
]
<=
'z'
||
follow
[
1
]
>=
'A'
&&
follow
[
1
]
<=
'Z'
{
return
l
.
errorf
(
"unknown number base: %s. possible options are x (hex) o (octal) b (binary)"
,
string
(
follow
[
1
]))
}
}
if
isValidRune
!=
nil
{
l
.
next
()
l
.
next
()
digitSeen
:=
false
for
{
next
:=
l
.
peek
()
if
!
isValidRune
(
next
)
{
break
}
digitSeen
=
true
l
.
next
()
}
if
!
digitSeen
{
return
l
.
errorf
(
"number needs at least one digit"
)
}
l
.
emit
(
tokenInteger
)
return
l
.
lexRvalue
}
}
}
if
r
==
'+'
||
r
==
'-'
{
l
.
next
()
if
l
.
follow
(
"inf"
)
{
return
l
.
lexInf
}
if
l
.
follow
(
"nan"
)
{
return
l
.
lexNan
}
}
pointSeen
:=
false
expSeen
:=
false
digitSeen
:=
false
for
{
next
:=
l
.
peek
()
if
next
==
'.'
{
if
pointSeen
{
return
l
.
errorf
(
"cannot have two dots in one float"
)
}
l
.
next
()
if
!
isDigit
(
l
.
peek
())
{
return
l
.
errorf
(
"float cannot end with a dot"
)
}
pointSeen
=
true
}
else
if
next
==
'e'
||
next
==
'E'
{
expSeen
=
true
l
.
next
()
r
:=
l
.
peek
()
if
r
==
'+'
||
r
==
'-'
{
l
.
next
()
}
}
else
if
isDigit
(
next
)
{
digitSeen
=
true
l
.
next
()
}
else
if
next
==
'_'
{
l
.
next
()
}
else
{
break
}
if
pointSeen
&&
!
digitSeen
{
return
l
.
errorf
(
"cannot start float with a dot"
)
}
}
if
!
digitSeen
{
return
l
.
errorf
(
"no digit in that number"
)
}
if
pointSeen
||
expSeen
{
l
.
emit
(
tokenFloat
)
}
else
{
l
.
emit
(
tokenInteger
)
}
return
l
.
lexRvalue
}
func
(
l
*
tomlLexer
)
run
()
{
for
state
:=
l
.
lexVoid
;
state
!=
nil
;
{
state
=
state
()
}
}
// Entry point
func
lexToml
(
inputBytes
[]
byte
)
[]
token
{
runes
:=
bytes
.
Runes
(
inputBytes
)
l
:=
&
tomlLexer
{
input
:
runes
,
tokens
:
make
([]
token
,
0
,
256
),
line
:
1
,
col
:
1
,
endbufferLine
:
1
,
endbufferCol
:
1
,
}
l
.
run
()
return
l
.
tokens
}
vendor/github.com/pelletier/go-toml/localtime.go
0 → 100644
View file @
d7e13eb9
// 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
}
vendor/github.com/pelletier/go-toml/marshal.go
0 → 100644
View file @
d7e13eb9
package
toml
import
(
"bytes"
"encoding"
"errors"
"fmt"
"io"
"reflect"
"sort"
"strconv"
"strings"
"time"
)
const
(
tagFieldName
=
"toml"
tagFieldComment
=
"comment"
tagCommented
=
"commented"
tagMultiline
=
"multiline"
tagLiteral
=
"literal"
tagDefault
=
"default"
)
type
tomlOpts
struct
{
name
string
nameFromTag
bool
comment
string
commented
bool
multiline
bool
literal
bool
include
bool
omitempty
bool
defaultValue
string
}
type
encOpts
struct
{
quoteMapKeys
bool
arraysOneElementPerLine
bool
}
var
encOptsDefaults
=
encOpts
{
quoteMapKeys
:
false
,
}
type
annotation
struct
{
tag
string
comment
string
commented
string
multiline
string
literal
string
defaultValue
string
}
var
annotationDefault
=
annotation
{
tag
:
tagFieldName
,
comment
:
tagFieldComment
,
commented
:
tagCommented
,
multiline
:
tagMultiline
,
literal
:
tagLiteral
,
defaultValue
:
tagDefault
,
}
type
MarshalOrder
int
// Orders the Encoder can write the fields to the output stream.
const
(
// Sort fields alphabetically.
OrderAlphabetical
MarshalOrder
=
iota
+
1
// Preserve the order the fields are encountered. For example, the order of fields in
// a struct.
OrderPreserve
)
var
timeType
=
reflect
.
TypeOf
(
time
.
Time
{})
var
marshalerType
=
reflect
.
TypeOf
(
new
(
Marshaler
))
.
Elem
()
var
unmarshalerType
=
reflect
.
TypeOf
(
new
(
Unmarshaler
))
.
Elem
()
var
textMarshalerType
=
reflect
.
TypeOf
(
new
(
encoding
.
TextMarshaler
))
.
Elem
()
var
textUnmarshalerType
=
reflect
.
TypeOf
(
new
(
encoding
.
TextUnmarshaler
))
.
Elem
()
var
localDateType
=
reflect
.
TypeOf
(
LocalDate
{})
var
localTimeType
=
reflect
.
TypeOf
(
LocalTime
{})
var
localDateTimeType
=
reflect
.
TypeOf
(
LocalDateTime
{})
var
mapStringInterfaceType
=
reflect
.
TypeOf
(
map
[
string
]
interface
{}{})
// Check if the given marshal type maps to a Tree primitive
func
isPrimitive
(
mtype
reflect
.
Type
)
bool
{
switch
mtype
.
Kind
()
{
case
reflect
.
Ptr
:
return
isPrimitive
(
mtype
.
Elem
())
case
reflect
.
Bool
:
return
true
case
reflect
.
Int
,
reflect
.
Int8
,
reflect
.
Int16
,
reflect
.
Int32
,
reflect
.
Int64
:
return
true
case
reflect
.
Uint
,
reflect
.
Uint8
,
reflect
.
Uint16
,
reflect
.
Uint32
,
reflect
.
Uint64
:
return
true
case
reflect
.
Float32
,
reflect
.
Float64
:
return
true
case
reflect
.
String
:
return
true
case
reflect
.
Struct
:
return
isTimeType
(
mtype
)
default
:
return
false
}
}
func
isTimeType
(
mtype
reflect
.
Type
)
bool
{
return
mtype
==
timeType
||
mtype
==
localDateType
||
mtype
==
localDateTimeType
||
mtype
==
localTimeType
}
// Check if the given marshal type maps to a Tree slice or array
func
isTreeSequence
(
mtype
reflect
.
Type
)
bool
{
switch
mtype
.
Kind
()
{
case
reflect
.
Ptr
:
return
isTreeSequence
(
mtype
.
Elem
())
case
reflect
.
Slice
,
reflect
.
Array
:
return
isTree
(
mtype
.
Elem
())
default
:
return
false
}
}
// Check if the given marshal type maps to a slice or array of a custom marshaler type
func
isCustomMarshalerSequence
(
mtype
reflect
.
Type
)
bool
{
switch
mtype
.
Kind
()
{
case
reflect
.
Ptr
:
return
isCustomMarshalerSequence
(
mtype
.
Elem
())
case
reflect
.
Slice
,
reflect
.
Array
:
return
isCustomMarshaler
(
mtype
.
Elem
())
||
isCustomMarshaler
(
reflect
.
New
(
mtype
.
Elem
())
.
Type
())
default
:
return
false
}
}
// Check if the given marshal type maps to a slice or array of a text marshaler type
func
isTextMarshalerSequence
(
mtype
reflect
.
Type
)
bool
{
switch
mtype
.
Kind
()
{
case
reflect
.
Ptr
:
return
isTextMarshalerSequence
(
mtype
.
Elem
())
case
reflect
.
Slice
,
reflect
.
Array
:
return
isTextMarshaler
(
mtype
.
Elem
())
||
isTextMarshaler
(
reflect
.
New
(
mtype
.
Elem
())
.
Type
())
default
:
return
false
}
}
// Check if the given marshal type maps to a non-Tree slice or array
func
isOtherSequence
(
mtype
reflect
.
Type
)
bool
{
switch
mtype
.
Kind
()
{
case
reflect
.
Ptr
:
return
isOtherSequence
(
mtype
.
Elem
())
case
reflect
.
Slice
,
reflect
.
Array
:
return
!
isTreeSequence
(
mtype
)
default
:
return
false
}
}
// Check if the given marshal type maps to a Tree
func
isTree
(
mtype
reflect
.
Type
)
bool
{
switch
mtype
.
Kind
()
{
case
reflect
.
Ptr
:
return
isTree
(
mtype
.
Elem
())
case
reflect
.
Map
:
return
true
case
reflect
.
Struct
:
return
!
isPrimitive
(
mtype
)
default
:
return
false
}
}
func
isCustomMarshaler
(
mtype
reflect
.
Type
)
bool
{
return
mtype
.
Implements
(
marshalerType
)
}
func
callCustomMarshaler
(
mval
reflect
.
Value
)
([]
byte
,
error
)
{
return
mval
.
Interface
()
.
(
Marshaler
)
.
MarshalTOML
()
}
func
isTextMarshaler
(
mtype
reflect
.
Type
)
bool
{
return
mtype
.
Implements
(
textMarshalerType
)
&&
!
isTimeType
(
mtype
)
}
func
callTextMarshaler
(
mval
reflect
.
Value
)
([]
byte
,
error
)
{
return
mval
.
Interface
()
.
(
encoding
.
TextMarshaler
)
.
MarshalText
()
}
func
isCustomUnmarshaler
(
mtype
reflect
.
Type
)
bool
{
return
mtype
.
Implements
(
unmarshalerType
)
}
func
callCustomUnmarshaler
(
mval
reflect
.
Value
,
tval
interface
{})
error
{
return
mval
.
Interface
()
.
(
Unmarshaler
)
.
UnmarshalTOML
(
tval
)
}
func
isTextUnmarshaler
(
mtype
reflect
.
Type
)
bool
{
return
mtype
.
Implements
(
textUnmarshalerType
)
}
func
callTextUnmarshaler
(
mval
reflect
.
Value
,
text
[]
byte
)
error
{
return
mval
.
Interface
()
.
(
encoding
.
TextUnmarshaler
)
.
UnmarshalText
(
text
)
}
// Marshaler is the interface implemented by types that
// can marshal themselves into valid TOML.
type
Marshaler
interface
{
MarshalTOML
()
([]
byte
,
error
)
}
// Unmarshaler is the interface implemented by types that
// can unmarshal a TOML description of themselves.
type
Unmarshaler
interface
{
UnmarshalTOML
(
interface
{})
error
}
/*
Marshal returns the TOML encoding of v. Behavior is similar to the Go json
encoder, except that there is no concept of a Marshaler interface or MarshalTOML
function for sub-structs, and currently only definite types can be marshaled
(i.e. no `interface{}`).
The following struct annotations are supported:
toml:"Field" Overrides the field's name to output.
omitempty When set, empty values and groups are not emitted.
comment:"comment" Emits a # comment on the same line. This supports new lines.
commented:"true" Emits the value as commented.
Note that pointers are automatically assigned the "omitempty" option, as TOML
explicitly does not handle null values (saying instead the label should be
dropped).
Tree structural types and corresponding marshal types:
*Tree (*)struct, (*)map[string]interface{}
[]*Tree (*)[](*)struct, (*)[](*)map[string]interface{}
[]interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{})
interface{} (*)primitive
Tree primitive types and corresponding marshal types:
uint64 uint, uint8-uint64, pointers to same
int64 int, int8-uint64, pointers to same
float64 float32, float64, pointers to same
string string, pointers to same
bool bool, pointers to same
time.LocalTime time.LocalTime{}, pointers to same
For additional flexibility, use the Encoder API.
*/
func
Marshal
(
v
interface
{})
([]
byte
,
error
)
{
return
NewEncoder
(
nil
)
.
marshal
(
v
)
}
// Encoder writes TOML values to an output stream.
type
Encoder
struct
{
w
io
.
Writer
encOpts
annotation
line
int
col
int
order
MarshalOrder
promoteAnon
bool
compactComments
bool
indentation
string
}
// NewEncoder returns a new encoder that writes to w.
func
NewEncoder
(
w
io
.
Writer
)
*
Encoder
{
return
&
Encoder
{
w
:
w
,
encOpts
:
encOptsDefaults
,
annotation
:
annotationDefault
,
line
:
0
,
col
:
1
,
order
:
OrderAlphabetical
,
indentation
:
" "
,
}
}
// Encode writes the TOML encoding of v to the stream.
//
// See the documentation for Marshal for details.
func
(
e
*
Encoder
)
Encode
(
v
interface
{})
error
{
b
,
err
:=
e
.
marshal
(
v
)
if
err
!=
nil
{
return
err
}
if
_
,
err
:=
e
.
w
.
Write
(
b
);
err
!=
nil
{
return
err
}
return
nil
}
// QuoteMapKeys sets up the encoder to encode
// maps with string type keys with quoted TOML keys.
//
// This relieves the character limitations on map keys.
func
(
e
*
Encoder
)
QuoteMapKeys
(
v
bool
)
*
Encoder
{
e
.
quoteMapKeys
=
v
return
e
}
// ArraysWithOneElementPerLine sets up the encoder to encode arrays
// with more than one element on multiple lines instead of one.
//
// For example:
//
// A = [1,2,3]
//
// Becomes
//
// A = [
// 1,
// 2,
// 3,
// ]
func
(
e
*
Encoder
)
ArraysWithOneElementPerLine
(
v
bool
)
*
Encoder
{
e
.
arraysOneElementPerLine
=
v
return
e
}
// Order allows to change in which order fields will be written to the output stream.
func
(
e
*
Encoder
)
Order
(
ord
MarshalOrder
)
*
Encoder
{
e
.
order
=
ord
return
e
}
// Indentation allows to change indentation when marshalling.
func
(
e
*
Encoder
)
Indentation
(
indent
string
)
*
Encoder
{
e
.
indentation
=
indent
return
e
}
// SetTagName allows changing default tag "toml"
func
(
e
*
Encoder
)
SetTagName
(
v
string
)
*
Encoder
{
e
.
tag
=
v
return
e
}
// SetTagComment allows changing default tag "comment"
func
(
e
*
Encoder
)
SetTagComment
(
v
string
)
*
Encoder
{
e
.
comment
=
v
return
e
}
// SetTagCommented allows changing default tag "commented"
func
(
e
*
Encoder
)
SetTagCommented
(
v
string
)
*
Encoder
{
e
.
commented
=
v
return
e
}
// SetTagMultiline allows changing default tag "multiline"
func
(
e
*
Encoder
)
SetTagMultiline
(
v
string
)
*
Encoder
{
e
.
multiline
=
v
return
e
}
// PromoteAnonymous allows to change how anonymous struct fields are marshaled.
// Usually, they are marshaled as if the inner exported fields were fields in
// the outer struct. However, if an anonymous struct field is given a name in
// its TOML tag, it is treated like a regular struct field with that name.
// rather than being anonymous.
//
// In case anonymous promotion is enabled, all anonymous structs are promoted
// and treated like regular struct fields.
func
(
e
*
Encoder
)
PromoteAnonymous
(
promote
bool
)
*
Encoder
{
e
.
promoteAnon
=
promote
return
e
}
// CompactComments removes the new line before each comment in the tree.
func
(
e
*
Encoder
)
CompactComments
(
cc
bool
)
*
Encoder
{
e
.
compactComments
=
cc
return
e
}
func
(
e
*
Encoder
)
marshal
(
v
interface
{})
([]
byte
,
error
)
{
// Check if indentation is valid
for
_
,
char
:=
range
e
.
indentation
{
if
!
isSpace
(
char
)
{
return
[]
byte
{},
fmt
.
Errorf
(
"invalid indentation: must only contains space or tab characters"
)
}
}
mtype
:=
reflect
.
TypeOf
(
v
)
if
mtype
==
nil
{
return
[]
byte
{},
errors
.
New
(
"nil cannot be marshaled to TOML"
)
}
switch
mtype
.
Kind
()
{
case
reflect
.
Struct
,
reflect
.
Map
:
case
reflect
.
Ptr
:
if
mtype
.
Elem
()
.
Kind
()
!=
reflect
.
Struct
{
return
[]
byte
{},
errors
.
New
(
"Only pointer to struct can be marshaled to TOML"
)
}
if
reflect
.
ValueOf
(
v
)
.
IsNil
()
{
return
[]
byte
{},
errors
.
New
(
"nil pointer cannot be marshaled to TOML"
)
}
default
:
return
[]
byte
{},
errors
.
New
(
"Only a struct or map can be marshaled to TOML"
)
}
sval
:=
reflect
.
ValueOf
(
v
)
if
isCustomMarshaler
(
mtype
)
{
return
callCustomMarshaler
(
sval
)
}
if
isTextMarshaler
(
mtype
)
{
return
callTextMarshaler
(
sval
)
}
t
,
err
:=
e
.
valueToTree
(
mtype
,
sval
)
if
err
!=
nil
{
return
[]
byte
{},
err
}
var
buf
bytes
.
Buffer
_
,
err
=
t
.
writeToOrdered
(
&
buf
,
""
,
""
,
0
,
e
.
arraysOneElementPerLine
,
e
.
order
,
e
.
indentation
,
e
.
compactComments
,
false
)
return
buf
.
Bytes
(),
err
}
// Create next tree with a position based on Encoder.line
func
(
e
*
Encoder
)
nextTree
()
*
Tree
{
return
newTreeWithPosition
(
Position
{
Line
:
e
.
line
,
Col
:
1
})
}
// Convert given marshal struct or map value to toml tree
func
(
e
*
Encoder
)
valueToTree
(
mtype
reflect
.
Type
,
mval
reflect
.
Value
)
(
*
Tree
,
error
)
{
if
mtype
.
Kind
()
==
reflect
.
Ptr
{
return
e
.
valueToTree
(
mtype
.
Elem
(),
mval
.
Elem
())
}
tval
:=
e
.
nextTree
()
switch
mtype
.
Kind
()
{
case
reflect
.
Struct
:
switch
mval
.
Interface
()
.
(
type
)
{
case
Tree
:
reflect
.
ValueOf
(
tval
)
.
Elem
()
.
Set
(
mval
)
default
:
for
i
:=
0
;
i
<
mtype
.
NumField
();
i
++
{
mtypef
,
mvalf
:=
mtype
.
Field
(
i
),
mval
.
Field
(
i
)
opts
:=
tomlOptions
(
mtypef
,
e
.
annotation
)
if
opts
.
include
&&
((
mtypef
.
Type
.
Kind
()
!=
reflect
.
Interface
&&
!
opts
.
omitempty
)
||
!
isZero
(
mvalf
))
{
val
,
err
:=
e
.
valueToToml
(
mtypef
.
Type
,
mvalf
)
if
err
!=
nil
{
return
nil
,
err
}
if
tree
,
ok
:=
val
.
(
*
Tree
);
ok
&&
mtypef
.
Anonymous
&&
!
opts
.
nameFromTag
&&
!
e
.
promoteAnon
{
e
.
appendTree
(
tval
,
tree
)
}
else
{
val
=
e
.
wrapTomlValue
(
val
,
tval
)
tval
.
SetPathWithOptions
([]
string
{
opts
.
name
},
SetOptions
{
Comment
:
opts
.
comment
,
Commented
:
opts
.
commented
,
Multiline
:
opts
.
multiline
,
Literal
:
opts
.
literal
,
},
val
)
}
}
}
}
case
reflect
.
Map
:
keys
:=
mval
.
MapKeys
()
if
e
.
order
==
OrderPreserve
&&
len
(
keys
)
>
0
{
// Sorting []reflect.Value is not straight forward.
//
// OrderPreserve will support deterministic results when string is used
// as the key to maps.
typ
:=
keys
[
0
]
.
Type
()
kind
:=
keys
[
0
]
.
Kind
()
if
kind
==
reflect
.
String
{
ikeys
:=
make
([]
string
,
len
(
keys
))
for
i
:=
range
keys
{
ikeys
[
i
]
=
keys
[
i
]
.
Interface
()
.
(
string
)
}
sort
.
Strings
(
ikeys
)
for
i
:=
range
ikeys
{
keys
[
i
]
=
reflect
.
ValueOf
(
ikeys
[
i
])
.
Convert
(
typ
)
}
}
}
for
_
,
key
:=
range
keys
{
mvalf
:=
mval
.
MapIndex
(
key
)
if
(
mtype
.
Elem
()
.
Kind
()
==
reflect
.
Ptr
||
mtype
.
Elem
()
.
Kind
()
==
reflect
.
Interface
)
&&
mvalf
.
IsNil
()
{
continue
}
val
,
err
:=
e
.
valueToToml
(
mtype
.
Elem
(),
mvalf
)
if
err
!=
nil
{
return
nil
,
err
}
val
=
e
.
wrapTomlValue
(
val
,
tval
)
if
e
.
quoteMapKeys
{
keyStr
,
err
:=
tomlValueStringRepresentation
(
key
.
String
(),
""
,
""
,
e
.
order
,
e
.
arraysOneElementPerLine
)
if
err
!=
nil
{
return
nil
,
err
}
tval
.
SetPath
([]
string
{
keyStr
},
val
)
}
else
{
tval
.
SetPath
([]
string
{
key
.
String
()},
val
)
}
}
}
return
tval
,
nil
}
// Convert given marshal slice to slice of Toml trees
func
(
e
*
Encoder
)
valueToTreeSlice
(
mtype
reflect
.
Type
,
mval
reflect
.
Value
)
([]
*
Tree
,
error
)
{
tval
:=
make
([]
*
Tree
,
mval
.
Len
(),
mval
.
Len
())
for
i
:=
0
;
i
<
mval
.
Len
();
i
++
{
val
,
err
:=
e
.
valueToTree
(
mtype
.
Elem
(),
mval
.
Index
(
i
))
if
err
!=
nil
{
return
nil
,
err
}
tval
[
i
]
=
val
}
return
tval
,
nil
}
// Convert given marshal slice to slice of toml values
func
(
e
*
Encoder
)
valueToOtherSlice
(
mtype
reflect
.
Type
,
mval
reflect
.
Value
)
(
interface
{},
error
)
{
tval
:=
make
([]
interface
{},
mval
.
Len
(),
mval
.
Len
())
for
i
:=
0
;
i
<
mval
.
Len
();
i
++
{
val
,
err
:=
e
.
valueToToml
(
mtype
.
Elem
(),
mval
.
Index
(
i
))
if
err
!=
nil
{
return
nil
,
err
}
tval
[
i
]
=
val
}
return
tval
,
nil
}
// Convert given marshal value to toml value
func
(
e
*
Encoder
)
valueToToml
(
mtype
reflect
.
Type
,
mval
reflect
.
Value
)
(
interface
{},
error
)
{
if
mtype
.
Kind
()
==
reflect
.
Ptr
{
switch
{
case
isCustomMarshaler
(
mtype
)
:
return
callCustomMarshaler
(
mval
)
case
isTextMarshaler
(
mtype
)
:
b
,
err
:=
callTextMarshaler
(
mval
)
return
string
(
b
),
err
default
:
return
e
.
valueToToml
(
mtype
.
Elem
(),
mval
.
Elem
())
}
}
if
mtype
.
Kind
()
==
reflect
.
Interface
{
return
e
.
valueToToml
(
mval
.
Elem
()
.
Type
(),
mval
.
Elem
())
}
switch
{
case
isCustomMarshaler
(
mtype
)
:
return
callCustomMarshaler
(
mval
)
case
isTextMarshaler
(
mtype
)
:
b
,
err
:=
callTextMarshaler
(
mval
)
return
string
(
b
),
err
case
isTree
(
mtype
)
:
return
e
.
valueToTree
(
mtype
,
mval
)
case
isOtherSequence
(
mtype
),
isCustomMarshalerSequence
(
mtype
),
isTextMarshalerSequence
(
mtype
)
:
return
e
.
valueToOtherSlice
(
mtype
,
mval
)
case
isTreeSequence
(
mtype
)
:
return
e
.
valueToTreeSlice
(
mtype
,
mval
)
default
:
switch
mtype
.
Kind
()
{
case
reflect
.
Bool
:
return
mval
.
Bool
(),
nil
case
reflect
.
Int
,
reflect
.
Int8
,
reflect
.
Int16
,
reflect
.
Int32
,
reflect
.
Int64
:
if
mtype
.
Kind
()
==
reflect
.
Int64
&&
mtype
==
reflect
.
TypeOf
(
time
.
Duration
(
1
))
{
return
fmt
.
Sprint
(
mval
),
nil
}
return
mval
.
Int
(),
nil
case
reflect
.
Uint
,
reflect
.
Uint8
,
reflect
.
Uint16
,
reflect
.
Uint32
,
reflect
.
Uint64
:
return
mval
.
Uint
(),
nil
case
reflect
.
Float32
,
reflect
.
Float64
:
return
mval
.
Float
(),
nil
case
reflect
.
String
:
return
mval
.
String
(),
nil
case
reflect
.
Struct
:
return
mval
.
Interface
(),
nil
default
:
return
nil
,
fmt
.
Errorf
(
"Marshal can't handle %v(%v)"
,
mtype
,
mtype
.
Kind
())
}
}
}
func
(
e
*
Encoder
)
appendTree
(
t
,
o
*
Tree
)
error
{
for
key
,
value
:=
range
o
.
values
{
if
_
,
ok
:=
t
.
values
[
key
];
ok
{
continue
}
if
tomlValue
,
ok
:=
value
.
(
*
tomlValue
);
ok
{
tomlValue
.
position
.
Col
=
t
.
position
.
Col
}
t
.
values
[
key
]
=
value
}
return
nil
}
// Create a toml value with the current line number as the position line
func
(
e
*
Encoder
)
wrapTomlValue
(
val
interface
{},
parent
*
Tree
)
interface
{}
{
_
,
isTree
:=
val
.
(
*
Tree
)
_
,
isTreeS
:=
val
.
([]
*
Tree
)
if
isTree
||
isTreeS
{
e
.
line
++
return
val
}
ret
:=
&
tomlValue
{
value
:
val
,
position
:
Position
{
e
.
line
,
parent
.
position
.
Col
,
},
}
e
.
line
++
return
ret
}
// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v.
// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for
// sub-structs, and only definite types can be unmarshaled.
func
(
t
*
Tree
)
Unmarshal
(
v
interface
{})
error
{
d
:=
Decoder
{
tval
:
t
,
tagName
:
tagFieldName
}
return
d
.
unmarshal
(
v
)
}
// Marshal returns the TOML encoding of Tree.
// See Marshal() documentation for types mapping table.
func
(
t
*
Tree
)
Marshal
()
([]
byte
,
error
)
{
var
buf
bytes
.
Buffer
_
,
err
:=
t
.
WriteTo
(
&
buf
)
if
err
!=
nil
{
return
nil
,
err
}
return
buf
.
Bytes
(),
nil
}
// Unmarshal parses the TOML-encoded data and stores the result in the value
// pointed to by v. Behavior is similar to the Go json encoder, except that there
// is no concept of an Unmarshaler interface or UnmarshalTOML function for
// sub-structs, and currently only definite types can be unmarshaled to (i.e. no
// `interface{}`).
//
// The following struct annotations are supported:
//
// toml:"Field" Overrides the field's name to map to.
// default:"foo" Provides a default value.
//
// For default values, only fields of the following types are supported:
// * string
// * bool
// * int
// * int64
// * float64
//
// See Marshal() documentation for types mapping table.
func
Unmarshal
(
data
[]
byte
,
v
interface
{})
error
{
t
,
err
:=
LoadReader
(
bytes
.
NewReader
(
data
))
if
err
!=
nil
{
return
err
}
return
t
.
Unmarshal
(
v
)
}
// Decoder reads and decodes TOML values from an input stream.
type
Decoder
struct
{
r
io
.
Reader
tval
*
Tree
encOpts
tagName
string
strict
bool
visitor
visitorState
}
// NewDecoder returns a new decoder that reads from r.
func
NewDecoder
(
r
io
.
Reader
)
*
Decoder
{
return
&
Decoder
{
r
:
r
,
encOpts
:
encOptsDefaults
,
tagName
:
tagFieldName
,
}
}
// Decode reads a TOML-encoded value from it's input
// and unmarshals it in the value pointed at by v.
//
// See the documentation for Marshal for details.
func
(
d
*
Decoder
)
Decode
(
v
interface
{})
error
{
var
err
error
d
.
tval
,
err
=
LoadReader
(
d
.
r
)
if
err
!=
nil
{
return
err
}
return
d
.
unmarshal
(
v
)
}
// SetTagName allows changing default tag "toml"
func
(
d
*
Decoder
)
SetTagName
(
v
string
)
*
Decoder
{
d
.
tagName
=
v
return
d
}
// Strict allows changing to strict decoding. Any fields that are found in the
// input data and do not have a corresponding struct member cause an error.
func
(
d
*
Decoder
)
Strict
(
strict
bool
)
*
Decoder
{
d
.
strict
=
strict
return
d
}
func
(
d
*
Decoder
)
unmarshal
(
v
interface
{})
error
{
mtype
:=
reflect
.
TypeOf
(
v
)
if
mtype
==
nil
{
return
errors
.
New
(
"nil cannot be unmarshaled from TOML"
)
}
if
mtype
.
Kind
()
!=
reflect
.
Ptr
{
return
errors
.
New
(
"only a pointer to struct or map can be unmarshaled from TOML"
)
}
elem
:=
mtype
.
Elem
()
switch
elem
.
Kind
()
{
case
reflect
.
Struct
,
reflect
.
Map
:
case
reflect
.
Interface
:
elem
=
mapStringInterfaceType
default
:
return
errors
.
New
(
"only a pointer to struct or map can be unmarshaled from TOML"
)
}
if
reflect
.
ValueOf
(
v
)
.
IsNil
()
{
return
errors
.
New
(
"nil pointer cannot be unmarshaled from TOML"
)
}
vv
:=
reflect
.
ValueOf
(
v
)
.
Elem
()
if
d
.
strict
{
d
.
visitor
=
newVisitorState
(
d
.
tval
)
}
sval
,
err
:=
d
.
valueFromTree
(
elem
,
d
.
tval
,
&
vv
)
if
err
!=
nil
{
return
err
}
if
err
:=
d
.
visitor
.
validate
();
err
!=
nil
{
return
err
}
reflect
.
ValueOf
(
v
)
.
Elem
()
.
Set
(
sval
)
return
nil
}
// Convert toml tree to marshal struct or map, using marshal type. When mval1
// is non-nil, merge fields into the given value instead of allocating a new one.
func
(
d
*
Decoder
)
valueFromTree
(
mtype
reflect
.
Type
,
tval
*
Tree
,
mval1
*
reflect
.
Value
)
(
reflect
.
Value
,
error
)
{
if
mtype
.
Kind
()
==
reflect
.
Ptr
{
return
d
.
unwrapPointer
(
mtype
,
tval
,
mval1
)
}
// Check if pointer to value implements the Unmarshaler interface.
if
mvalPtr
:=
reflect
.
New
(
mtype
);
isCustomUnmarshaler
(
mvalPtr
.
Type
())
{
d
.
visitor
.
visitAll
()
if
tval
==
nil
{
return
mvalPtr
.
Elem
(),
nil
}
if
err
:=
callCustomUnmarshaler
(
mvalPtr
,
tval
.
ToMap
());
err
!=
nil
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"unmarshal toml: %v"
,
err
)
}
return
mvalPtr
.
Elem
(),
nil
}
var
mval
reflect
.
Value
switch
mtype
.
Kind
()
{
case
reflect
.
Struct
:
if
mval1
!=
nil
{
mval
=
*
mval1
}
else
{
mval
=
reflect
.
New
(
mtype
)
.
Elem
()
}
switch
mval
.
Interface
()
.
(
type
)
{
case
Tree
:
mval
.
Set
(
reflect
.
ValueOf
(
tval
)
.
Elem
())
default
:
for
i
:=
0
;
i
<
mtype
.
NumField
();
i
++
{
mtypef
:=
mtype
.
Field
(
i
)
an
:=
annotation
{
tag
:
d
.
tagName
}
opts
:=
tomlOptions
(
mtypef
,
an
)
if
!
opts
.
include
{
continue
}
baseKey
:=
opts
.
name
keysToTry
:=
[]
string
{
baseKey
,
strings
.
ToLower
(
baseKey
),
strings
.
ToTitle
(
baseKey
),
strings
.
ToLower
(
string
(
baseKey
[
0
]))
+
baseKey
[
1
:
],
}
found
:=
false
if
tval
!=
nil
{
for
_
,
key
:=
range
keysToTry
{
exists
:=
tval
.
HasPath
([]
string
{
key
})
if
!
exists
{
continue
}
d
.
visitor
.
push
(
key
)
val
:=
tval
.
GetPath
([]
string
{
key
})
fval
:=
mval
.
Field
(
i
)
mvalf
,
err
:=
d
.
valueFromToml
(
mtypef
.
Type
,
val
,
&
fval
)
if
err
!=
nil
{
return
mval
,
formatError
(
err
,
tval
.
GetPositionPath
([]
string
{
key
}))
}
mval
.
Field
(
i
)
.
Set
(
mvalf
)
found
=
true
d
.
visitor
.
pop
()
break
}
}
if
!
found
&&
opts
.
defaultValue
!=
""
{
mvalf
:=
mval
.
Field
(
i
)
var
val
interface
{}
var
err
error
switch
mvalf
.
Kind
()
{
case
reflect
.
String
:
val
=
opts
.
defaultValue
case
reflect
.
Bool
:
val
,
err
=
strconv
.
ParseBool
(
opts
.
defaultValue
)
case
reflect
.
Uint
:
val
,
err
=
strconv
.
ParseUint
(
opts
.
defaultValue
,
10
,
0
)
case
reflect
.
Uint8
:
val
,
err
=
strconv
.
ParseUint
(
opts
.
defaultValue
,
10
,
8
)
case
reflect
.
Uint16
:
val
,
err
=
strconv
.
ParseUint
(
opts
.
defaultValue
,
10
,
16
)
case
reflect
.
Uint32
:
val
,
err
=
strconv
.
ParseUint
(
opts
.
defaultValue
,
10
,
32
)
case
reflect
.
Uint64
:
val
,
err
=
strconv
.
ParseUint
(
opts
.
defaultValue
,
10
,
64
)
case
reflect
.
Int
:
val
,
err
=
strconv
.
ParseInt
(
opts
.
defaultValue
,
10
,
0
)
case
reflect
.
Int8
:
val
,
err
=
strconv
.
ParseInt
(
opts
.
defaultValue
,
10
,
8
)
case
reflect
.
Int16
:
val
,
err
=
strconv
.
ParseInt
(
opts
.
defaultValue
,
10
,
16
)
case
reflect
.
Int32
:
val
,
err
=
strconv
.
ParseInt
(
opts
.
defaultValue
,
10
,
32
)
case
reflect
.
Int64
:
// Check if the provided number has a non-numeric extension.
var
hasExtension
bool
if
len
(
opts
.
defaultValue
)
>
0
{
lastChar
:=
opts
.
defaultValue
[
len
(
opts
.
defaultValue
)
-
1
]
if
lastChar
<
'0'
||
lastChar
>
'9'
{
hasExtension
=
true
}
}
// If the value is a time.Duration with extension, parse as duration.
// If the value is an int64 or a time.Duration without extension, parse as number.
if
hasExtension
&&
mvalf
.
Type
()
.
String
()
==
"time.Duration"
{
val
,
err
=
time
.
ParseDuration
(
opts
.
defaultValue
)
}
else
{
val
,
err
=
strconv
.
ParseInt
(
opts
.
defaultValue
,
10
,
64
)
}
case
reflect
.
Float32
:
val
,
err
=
strconv
.
ParseFloat
(
opts
.
defaultValue
,
32
)
case
reflect
.
Float64
:
val
,
err
=
strconv
.
ParseFloat
(
opts
.
defaultValue
,
64
)
default
:
return
mvalf
,
fmt
.
Errorf
(
"unsupported field type for default option"
)
}
if
err
!=
nil
{
return
mvalf
,
err
}
mvalf
.
Set
(
reflect
.
ValueOf
(
val
)
.
Convert
(
mvalf
.
Type
()))
}
// save the old behavior above and try to check structs
if
!
found
&&
opts
.
defaultValue
==
""
&&
mtypef
.
Type
.
Kind
()
==
reflect
.
Struct
{
tmpTval
:=
tval
if
!
mtypef
.
Anonymous
{
tmpTval
=
nil
}
fval
:=
mval
.
Field
(
i
)
v
,
err
:=
d
.
valueFromTree
(
mtypef
.
Type
,
tmpTval
,
&
fval
)
if
err
!=
nil
{
return
v
,
err
}
mval
.
Field
(
i
)
.
Set
(
v
)
}
}
}
case
reflect
.
Map
:
mval
=
reflect
.
MakeMap
(
mtype
)
for
_
,
key
:=
range
tval
.
Keys
()
{
d
.
visitor
.
push
(
key
)
// TODO: path splits key
val
:=
tval
.
GetPath
([]
string
{
key
})
mvalf
,
err
:=
d
.
valueFromToml
(
mtype
.
Elem
(),
val
,
nil
)
if
err
!=
nil
{
return
mval
,
formatError
(
err
,
tval
.
GetPositionPath
([]
string
{
key
}))
}
mval
.
SetMapIndex
(
reflect
.
ValueOf
(
key
)
.
Convert
(
mtype
.
Key
()),
mvalf
)
d
.
visitor
.
pop
()
}
}
return
mval
,
nil
}
// Convert toml value to marshal struct/map slice, using marshal type
func
(
d
*
Decoder
)
valueFromTreeSlice
(
mtype
reflect
.
Type
,
tval
[]
*
Tree
)
(
reflect
.
Value
,
error
)
{
mval
,
err
:=
makeSliceOrArray
(
mtype
,
len
(
tval
))
if
err
!=
nil
{
return
mval
,
err
}
for
i
:=
0
;
i
<
len
(
tval
);
i
++
{
d
.
visitor
.
push
(
strconv
.
Itoa
(
i
))
val
,
err
:=
d
.
valueFromTree
(
mtype
.
Elem
(),
tval
[
i
],
nil
)
if
err
!=
nil
{
return
mval
,
err
}
mval
.
Index
(
i
)
.
Set
(
val
)
d
.
visitor
.
pop
()
}
return
mval
,
nil
}
// Convert toml value to marshal primitive slice, using marshal type
func
(
d
*
Decoder
)
valueFromOtherSlice
(
mtype
reflect
.
Type
,
tval
[]
interface
{})
(
reflect
.
Value
,
error
)
{
mval
,
err
:=
makeSliceOrArray
(
mtype
,
len
(
tval
))
if
err
!=
nil
{
return
mval
,
err
}
for
i
:=
0
;
i
<
len
(
tval
);
i
++
{
val
,
err
:=
d
.
valueFromToml
(
mtype
.
Elem
(),
tval
[
i
],
nil
)
if
err
!=
nil
{
return
mval
,
err
}
mval
.
Index
(
i
)
.
Set
(
val
)
}
return
mval
,
nil
}
// Convert toml value to marshal primitive slice, using marshal type
func
(
d
*
Decoder
)
valueFromOtherSliceI
(
mtype
reflect
.
Type
,
tval
interface
{})
(
reflect
.
Value
,
error
)
{
val
:=
reflect
.
ValueOf
(
tval
)
length
:=
val
.
Len
()
mval
,
err
:=
makeSliceOrArray
(
mtype
,
length
)
if
err
!=
nil
{
return
mval
,
err
}
for
i
:=
0
;
i
<
length
;
i
++
{
val
,
err
:=
d
.
valueFromToml
(
mtype
.
Elem
(),
val
.
Index
(
i
)
.
Interface
(),
nil
)
if
err
!=
nil
{
return
mval
,
err
}
mval
.
Index
(
i
)
.
Set
(
val
)
}
return
mval
,
nil
}
// Create a new slice or a new array with specified length
func
makeSliceOrArray
(
mtype
reflect
.
Type
,
tLength
int
)
(
reflect
.
Value
,
error
)
{
var
mval
reflect
.
Value
switch
mtype
.
Kind
()
{
case
reflect
.
Slice
:
mval
=
reflect
.
MakeSlice
(
mtype
,
tLength
,
tLength
)
case
reflect
.
Array
:
mval
=
reflect
.
New
(
reflect
.
ArrayOf
(
mtype
.
Len
(),
mtype
.
Elem
()))
.
Elem
()
if
tLength
>
mtype
.
Len
()
{
return
mval
,
fmt
.
Errorf
(
"unmarshal: TOML array length (%v) exceeds destination array length (%v)"
,
tLength
,
mtype
.
Len
())
}
}
return
mval
,
nil
}
// Convert toml value to marshal value, using marshal type. When mval1 is non-nil
// and the given type is a struct value, merge fields into it.
func
(
d
*
Decoder
)
valueFromToml
(
mtype
reflect
.
Type
,
tval
interface
{},
mval1
*
reflect
.
Value
)
(
reflect
.
Value
,
error
)
{
if
mtype
.
Kind
()
==
reflect
.
Ptr
{
return
d
.
unwrapPointer
(
mtype
,
tval
,
mval1
)
}
switch
t
:=
tval
.
(
type
)
{
case
*
Tree
:
var
mval11
*
reflect
.
Value
if
mtype
.
Kind
()
==
reflect
.
Struct
{
mval11
=
mval1
}
if
isTree
(
mtype
)
{
return
d
.
valueFromTree
(
mtype
,
t
,
mval11
)
}
if
mtype
.
Kind
()
==
reflect
.
Interface
{
if
mval1
==
nil
||
mval1
.
IsNil
()
{
return
d
.
valueFromTree
(
reflect
.
TypeOf
(
map
[
string
]
interface
{}{}),
t
,
nil
)
}
else
{
return
d
.
valueFromToml
(
mval1
.
Elem
()
.
Type
(),
t
,
nil
)
}
}
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to a tree"
,
tval
,
tval
)
case
[]
*
Tree
:
if
isTreeSequence
(
mtype
)
{
return
d
.
valueFromTreeSlice
(
mtype
,
t
)
}
if
mtype
.
Kind
()
==
reflect
.
Interface
{
if
mval1
==
nil
||
mval1
.
IsNil
()
{
return
d
.
valueFromTreeSlice
(
reflect
.
TypeOf
([]
map
[
string
]
interface
{}{}),
t
)
}
else
{
ival
:=
mval1
.
Elem
()
return
d
.
valueFromToml
(
mval1
.
Elem
()
.
Type
(),
t
,
&
ival
)
}
}
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to trees"
,
tval
,
tval
)
case
[]
interface
{}
:
d
.
visitor
.
visit
()
if
isOtherSequence
(
mtype
)
{
return
d
.
valueFromOtherSlice
(
mtype
,
t
)
}
if
mtype
.
Kind
()
==
reflect
.
Interface
{
if
mval1
==
nil
||
mval1
.
IsNil
()
{
return
d
.
valueFromOtherSlice
(
reflect
.
TypeOf
([]
interface
{}{}),
t
)
}
else
{
ival
:=
mval1
.
Elem
()
return
d
.
valueFromToml
(
mval1
.
Elem
()
.
Type
(),
t
,
&
ival
)
}
}
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to a slice"
,
tval
,
tval
)
default
:
d
.
visitor
.
visit
()
mvalPtr
:=
reflect
.
New
(
mtype
)
// Check if pointer to value implements the Unmarshaler interface.
if
isCustomUnmarshaler
(
mvalPtr
.
Type
())
{
if
err
:=
callCustomUnmarshaler
(
mvalPtr
,
tval
);
err
!=
nil
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"unmarshal toml: %v"
,
err
)
}
return
mvalPtr
.
Elem
(),
nil
}
// Check if pointer to value implements the encoding.TextUnmarshaler.
if
isTextUnmarshaler
(
mvalPtr
.
Type
())
&&
!
isTimeType
(
mtype
)
{
if
err
:=
d
.
unmarshalText
(
tval
,
mvalPtr
);
err
!=
nil
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"unmarshal text: %v"
,
err
)
}
return
mvalPtr
.
Elem
(),
nil
}
switch
mtype
.
Kind
()
{
case
reflect
.
Bool
,
reflect
.
Struct
:
val
:=
reflect
.
ValueOf
(
tval
)
switch
val
.
Type
()
{
case
localDateType
:
localDate
:=
val
.
Interface
()
.
(
LocalDate
)
switch
mtype
{
case
timeType
:
return
reflect
.
ValueOf
(
time
.
Date
(
localDate
.
Year
,
localDate
.
Month
,
localDate
.
Day
,
0
,
0
,
0
,
0
,
time
.
Local
)),
nil
}
case
localDateTimeType
:
localDateTime
:=
val
.
Interface
()
.
(
LocalDateTime
)
switch
mtype
{
case
timeType
:
return
reflect
.
ValueOf
(
time
.
Date
(
localDateTime
.
Date
.
Year
,
localDateTime
.
Date
.
Month
,
localDateTime
.
Date
.
Day
,
localDateTime
.
Time
.
Hour
,
localDateTime
.
Time
.
Minute
,
localDateTime
.
Time
.
Second
,
localDateTime
.
Time
.
Nanosecond
,
time
.
Local
)),
nil
}
}
// if this passes for when mtype is reflect.Struct, tval is a time.LocalTime
if
!
val
.
Type
()
.
ConvertibleTo
(
mtype
)
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v"
,
tval
,
tval
,
mtype
.
String
())
}
return
val
.
Convert
(
mtype
),
nil
case
reflect
.
String
:
val
:=
reflect
.
ValueOf
(
tval
)
// stupidly, int64 is convertible to string. So special case this.
if
!
val
.
Type
()
.
ConvertibleTo
(
mtype
)
||
val
.
Kind
()
==
reflect
.
Int64
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v"
,
tval
,
tval
,
mtype
.
String
())
}
return
val
.
Convert
(
mtype
),
nil
case
reflect
.
Int
,
reflect
.
Int8
,
reflect
.
Int16
,
reflect
.
Int32
,
reflect
.
Int64
:
val
:=
reflect
.
ValueOf
(
tval
)
if
mtype
.
Kind
()
==
reflect
.
Int64
&&
mtype
==
reflect
.
TypeOf
(
time
.
Duration
(
1
))
&&
val
.
Kind
()
==
reflect
.
String
{
d
,
err
:=
time
.
ParseDuration
(
val
.
String
())
if
err
!=
nil
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v. %s"
,
tval
,
tval
,
mtype
.
String
(),
err
)
}
return
reflect
.
ValueOf
(
d
),
nil
}
if
!
val
.
Type
()
.
ConvertibleTo
(
mtype
)
||
val
.
Kind
()
==
reflect
.
Float64
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v"
,
tval
,
tval
,
mtype
.
String
())
}
if
reflect
.
Indirect
(
reflect
.
New
(
mtype
))
.
OverflowInt
(
val
.
Convert
(
reflect
.
TypeOf
(
int64
(
0
)))
.
Int
())
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"%v(%T) would overflow %v"
,
tval
,
tval
,
mtype
.
String
())
}
return
val
.
Convert
(
mtype
),
nil
case
reflect
.
Uint
,
reflect
.
Uint8
,
reflect
.
Uint16
,
reflect
.
Uint32
,
reflect
.
Uint64
,
reflect
.
Uintptr
:
val
:=
reflect
.
ValueOf
(
tval
)
if
!
val
.
Type
()
.
ConvertibleTo
(
mtype
)
||
val
.
Kind
()
==
reflect
.
Float64
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v"
,
tval
,
tval
,
mtype
.
String
())
}
if
val
.
Type
()
.
Kind
()
!=
reflect
.
Uint64
&&
val
.
Convert
(
reflect
.
TypeOf
(
int
(
1
)))
.
Int
()
<
0
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"%v(%T) is negative so does not fit in %v"
,
tval
,
tval
,
mtype
.
String
())
}
if
reflect
.
Indirect
(
reflect
.
New
(
mtype
))
.
OverflowUint
(
val
.
Convert
(
reflect
.
TypeOf
(
uint64
(
0
)))
.
Uint
())
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"%v(%T) would overflow %v"
,
tval
,
tval
,
mtype
.
String
())
}
return
val
.
Convert
(
mtype
),
nil
case
reflect
.
Float32
,
reflect
.
Float64
:
val
:=
reflect
.
ValueOf
(
tval
)
if
!
val
.
Type
()
.
ConvertibleTo
(
mtype
)
||
val
.
Kind
()
==
reflect
.
Int64
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v"
,
tval
,
tval
,
mtype
.
String
())
}
if
reflect
.
Indirect
(
reflect
.
New
(
mtype
))
.
OverflowFloat
(
val
.
Convert
(
reflect
.
TypeOf
(
float64
(
0
)))
.
Float
())
{
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"%v(%T) would overflow %v"
,
tval
,
tval
,
mtype
.
String
())
}
return
val
.
Convert
(
mtype
),
nil
case
reflect
.
Interface
:
if
mval1
==
nil
||
mval1
.
IsNil
()
{
return
reflect
.
ValueOf
(
tval
),
nil
}
else
{
ival
:=
mval1
.
Elem
()
return
d
.
valueFromToml
(
mval1
.
Elem
()
.
Type
(),
t
,
&
ival
)
}
case
reflect
.
Slice
,
reflect
.
Array
:
if
isOtherSequence
(
mtype
)
&&
isOtherSequence
(
reflect
.
TypeOf
(
t
))
{
return
d
.
valueFromOtherSliceI
(
mtype
,
t
)
}
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v(%v)"
,
tval
,
tval
,
mtype
,
mtype
.
Kind
())
default
:
return
reflect
.
ValueOf
(
nil
),
fmt
.
Errorf
(
"Can't convert %v(%T) to %v(%v)"
,
tval
,
tval
,
mtype
,
mtype
.
Kind
())
}
}
}
func
(
d
*
Decoder
)
unwrapPointer
(
mtype
reflect
.
Type
,
tval
interface
{},
mval1
*
reflect
.
Value
)
(
reflect
.
Value
,
error
)
{
var
melem
*
reflect
.
Value
if
mval1
!=
nil
&&
!
mval1
.
IsNil
()
&&
(
mtype
.
Elem
()
.
Kind
()
==
reflect
.
Struct
||
mtype
.
Elem
()
.
Kind
()
==
reflect
.
Interface
)
{
elem
:=
mval1
.
Elem
()
melem
=
&
elem
}
val
,
err
:=
d
.
valueFromToml
(
mtype
.
Elem
(),
tval
,
melem
)
if
err
!=
nil
{
return
reflect
.
ValueOf
(
nil
),
err
}
mval
:=
reflect
.
New
(
mtype
.
Elem
())
mval
.
Elem
()
.
Set
(
val
)
return
mval
,
nil
}
func
(
d
*
Decoder
)
unmarshalText
(
tval
interface
{},
mval
reflect
.
Value
)
error
{
var
buf
bytes
.
Buffer
fmt
.
Fprint
(
&
buf
,
tval
)
return
callTextUnmarshaler
(
mval
,
buf
.
Bytes
())
}
func
tomlOptions
(
vf
reflect
.
StructField
,
an
annotation
)
tomlOpts
{
tag
:=
vf
.
Tag
.
Get
(
an
.
tag
)
parse
:=
strings
.
Split
(
tag
,
","
)
var
comment
string
if
c
:=
vf
.
Tag
.
Get
(
an
.
comment
);
c
!=
""
{
comment
=
c
}
commented
,
_
:=
strconv
.
ParseBool
(
vf
.
Tag
.
Get
(
an
.
commented
))
multiline
,
_
:=
strconv
.
ParseBool
(
vf
.
Tag
.
Get
(
an
.
multiline
))
literal
,
_
:=
strconv
.
ParseBool
(
vf
.
Tag
.
Get
(
an
.
literal
))
defaultValue
:=
vf
.
Tag
.
Get
(
tagDefault
)
result
:=
tomlOpts
{
name
:
vf
.
Name
,
nameFromTag
:
false
,
comment
:
comment
,
commented
:
commented
,
multiline
:
multiline
,
literal
:
literal
,
include
:
true
,
omitempty
:
false
,
defaultValue
:
defaultValue
,
}
if
parse
[
0
]
!=
""
{
if
parse
[
0
]
==
"-"
&&
len
(
parse
)
==
1
{
result
.
include
=
false
}
else
{
result
.
name
=
strings
.
Trim
(
parse
[
0
],
" "
)
result
.
nameFromTag
=
true
}
}
if
vf
.
PkgPath
!=
""
{
result
.
include
=
false
}
if
len
(
parse
)
>
1
&&
strings
.
Trim
(
parse
[
1
],
" "
)
==
"omitempty"
{
result
.
omitempty
=
true
}
if
vf
.
Type
.
Kind
()
==
reflect
.
Ptr
{
result
.
omitempty
=
true
}
return
result
}
func
isZero
(
val
reflect
.
Value
)
bool
{
switch
val
.
Type
()
.
Kind
()
{
case
reflect
.
Slice
,
reflect
.
Array
,
reflect
.
Map
:
return
val
.
Len
()
==
0
default
:
return
reflect
.
DeepEqual
(
val
.
Interface
(),
reflect
.
Zero
(
val
.
Type
())
.
Interface
())
}
}
func
formatError
(
err
error
,
pos
Position
)
error
{
if
err
.
Error
()[
0
]
==
'('
{
// Error already contains position information
return
err
}
return
fmt
.
Errorf
(
"%s: %s"
,
pos
,
err
)
}
// visitorState keeps track of which keys were unmarshaled.
type
visitorState
struct
{
tree
*
Tree
path
[]
string
keys
map
[
string
]
struct
{}
active
bool
}
func
newVisitorState
(
tree
*
Tree
)
visitorState
{
path
,
result
:=
[]
string
{},
map
[
string
]
struct
{}{}
insertKeys
(
path
,
result
,
tree
)
return
visitorState
{
tree
:
tree
,
path
:
path
[
:
0
],
keys
:
result
,
active
:
true
,
}
}
func
(
s
*
visitorState
)
push
(
key
string
)
{
if
s
.
active
{
s
.
path
=
append
(
s
.
path
,
key
)
}
}
func
(
s
*
visitorState
)
pop
()
{
if
s
.
active
{
s
.
path
=
s
.
path
[
:
len
(
s
.
path
)
-
1
]
}
}
func
(
s
*
visitorState
)
visit
()
{
if
s
.
active
{
delete
(
s
.
keys
,
strings
.
Join
(
s
.
path
,
"."
))
}
}
func
(
s
*
visitorState
)
visitAll
()
{
if
s
.
active
{
for
k
:=
range
s
.
keys
{
if
strings
.
HasPrefix
(
k
,
strings
.
Join
(
s
.
path
,
"."
))
{
delete
(
s
.
keys
,
k
)
}
}
}
}
func
(
s
*
visitorState
)
validate
()
error
{
if
!
s
.
active
{
return
nil
}
undecoded
:=
make
([]
string
,
0
,
len
(
s
.
keys
))
for
key
:=
range
s
.
keys
{
undecoded
=
append
(
undecoded
,
key
)
}
sort
.
Strings
(
undecoded
)
if
len
(
undecoded
)
>
0
{
return
fmt
.
Errorf
(
"undecoded keys: %q"
,
undecoded
)
}
return
nil
}
func
insertKeys
(
path
[]
string
,
m
map
[
string
]
struct
{},
tree
*
Tree
)
{
for
k
,
v
:=
range
tree
.
values
{
switch
node
:=
v
.
(
type
)
{
case
[]
*
Tree
:
for
i
,
item
:=
range
node
{
insertKeys
(
append
(
path
,
k
,
strconv
.
Itoa
(
i
)),
m
,
item
)
}
case
*
Tree
:
insertKeys
(
append
(
path
,
k
),
m
,
node
)
case
*
tomlValue
:
m
[
strings
.
Join
(
append
(
path
,
k
),
"."
)]
=
struct
{}{}
}
}
}
vendor/github.com/pelletier/go-toml/marshal_OrderPreserve_test.toml
0 → 100644
View file @
d7e13eb9
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"
vendor/github.com/pelletier/go-toml/marshal_test.toml
0 → 100644
View file @
d7e13eb9
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"
vendor/github.com/pelletier/go-toml/parser.go
0 → 100644
View file @
d7e13eb9
// 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
}
vendor/github.com/pelletier/go-toml/position.go
0 → 100644
View file @
d7e13eb9
// 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
}
vendor/github.com/pelletier/go-toml/token.go
0 → 100644
View file @
d7e13eb9
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'
)
}
vendor/github.com/pelletier/go-toml/toml.go
0 → 100644
View file @
d7e13eb9
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
)
}
vendor/github.com/pelletier/go-toml/tomlpub.go
0 → 100644
View file @
d7e13eb9
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
}
vendor/github.com/pelletier/go-toml/tomltree_create.go
0 → 100644
View file @
d7e13eb9
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
}
vendor/github.com/pelletier/go-toml/tomltree_write.go
0 → 100644
View file @
d7e13eb9
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
}
Prev
1
…
9
10
11
12
13
14
15
16
17
18
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment