Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65ca806488 | |||
| 5c94d86029 | |||
| b76eb62117 | |||
| 196ce3a1f6 | |||
| 9f8f82dfe8 | |||
| 661484ae7e | |||
| 34de94e6a8 | |||
| 88263a05cc | |||
| 1dbe20e76c | |||
| 05bf3807d3 | |||
| 06838de5d2 | |||
| db62263e3e | |||
| 2d866e3fae | |||
| 100799f7b7 | |||
| ecd155a62f | |||
| bcacc71a18 | |||
| 71c324cf7b | |||
| 4c840f1b8b | |||
| 5060c72d94 | |||
| 0a459e938d |
+20
-20
@@ -13,9 +13,9 @@ stages:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go 1.14"
|
||||
displayName: "Install Go 1.15"
|
||||
inputs:
|
||||
version: "1.14"
|
||||
version: "1.15"
|
||||
- script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/"
|
||||
- script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml
|
||||
- script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml
|
||||
@@ -36,9 +36,9 @@ stages:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go 1.14"
|
||||
displayName: "Install Go 1.15"
|
||||
inputs:
|
||||
version: "1.14"
|
||||
version: "1.15"
|
||||
- task: Go@0
|
||||
displayName: "go fmt ./..."
|
||||
inputs:
|
||||
@@ -51,9 +51,9 @@ stages:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go 1.14"
|
||||
displayName: "Install Go 1.15"
|
||||
inputs:
|
||||
version: "1.14"
|
||||
version: "1.15"
|
||||
- task: Go@0
|
||||
displayName: "Generate coverage"
|
||||
inputs:
|
||||
@@ -71,9 +71,9 @@ stages:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go 1.14"
|
||||
displayName: "Install Go 1.15"
|
||||
inputs:
|
||||
version: "1.14"
|
||||
version: "1.15"
|
||||
- script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/"
|
||||
- task: Bash@3
|
||||
inputs:
|
||||
@@ -86,9 +86,9 @@ stages:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go 1.14"
|
||||
displayName: "Install Go 1.15"
|
||||
inputs:
|
||||
version: "1.14"
|
||||
version: "1.15"
|
||||
- script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/"
|
||||
- script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml
|
||||
- script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml
|
||||
@@ -102,6 +102,15 @@ stages:
|
||||
displayName: "unit tests"
|
||||
strategy:
|
||||
matrix:
|
||||
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'
|
||||
linux 1.14:
|
||||
goVersion: '1.14'
|
||||
imageName: 'ubuntu-latest'
|
||||
@@ -111,15 +120,6 @@ stages:
|
||||
windows 1.14:
|
||||
goVersion: '1.14'
|
||||
imageName: 'windows-latest'
|
||||
linux 1.13:
|
||||
goVersion: '1.13'
|
||||
imageName: 'ubuntu-latest'
|
||||
mac 1.13:
|
||||
goVersion: '1.13'
|
||||
imageName: 'macOS-latest'
|
||||
windows 1.13:
|
||||
goVersion: '1.13'
|
||||
imageName: 'windows-latest'
|
||||
pool:
|
||||
vmImage: $(imageName)
|
||||
steps:
|
||||
@@ -155,7 +155,7 @@ stages:
|
||||
- task: GoTool@0
|
||||
displayName: "Install Go"
|
||||
inputs:
|
||||
version: 1.14
|
||||
version: 1.15
|
||||
- task: Bash@3
|
||||
inputs:
|
||||
targetType: inline
|
||||
|
||||
@@ -20,11 +20,15 @@ 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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package toml
|
||||
package benchmark
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
burntsushi "github.com/BurntSushi/toml"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"github.com/pelletier/go-toml"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type benchmarkDoc struct {
|
||||
@@ -124,7 +125,7 @@ func BenchmarkParseToml(b *testing.B) {
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := LoadReader(bytes.NewReader(fileBytes))
|
||||
_, err := toml.LoadReader(bytes.NewReader(fileBytes))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -139,7 +140,7 @@ func BenchmarkUnmarshalToml(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
target := benchmarkDoc{}
|
||||
err := Unmarshal(bytes, &target)
|
||||
err := toml.Unmarshal(bytes, &target)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
module github.com/pelletier/go-toml/benchmark
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/pelletier/go-toml v0.0.0
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
)
|
||||
|
||||
replace github.com/pelletier/go-toml => ../
|
||||
@@ -0,0 +1,8 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -2,8 +2,4 @@ module github.com/pelletier/go-toml
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
)
|
||||
require github.com/davecgh/go-spew v1.1.1
|
||||
|
||||
@@ -306,7 +306,7 @@ func (l *tomlLexer) lexComma() tomlLexStateFn {
|
||||
// Parse the key and emits its value without escape sequences.
|
||||
// bare keys, basic string keys and literal string keys are supported.
|
||||
func (l *tomlLexer) lexKey() tomlLexStateFn {
|
||||
growingString := ""
|
||||
var sb strings.Builder
|
||||
|
||||
for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() {
|
||||
if r == '"' {
|
||||
@@ -315,7 +315,9 @@ func (l *tomlLexer) lexKey() tomlLexStateFn {
|
||||
if err != nil {
|
||||
return l.errorf(err.Error())
|
||||
}
|
||||
growingString += "\"" + str + "\""
|
||||
sb.WriteString("\"")
|
||||
sb.WriteString(str)
|
||||
sb.WriteString("\"")
|
||||
l.next()
|
||||
continue
|
||||
} else if r == '\'' {
|
||||
@@ -324,41 +326,45 @@ func (l *tomlLexer) lexKey() tomlLexStateFn {
|
||||
if err != nil {
|
||||
return l.errorf(err.Error())
|
||||
}
|
||||
growingString += "'" + str + "'"
|
||||
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) {
|
||||
str := " "
|
||||
var str strings.Builder
|
||||
str.WriteString(" ")
|
||||
|
||||
// skip trailing whitespace
|
||||
l.next()
|
||||
for r = l.peek(); isSpace(r); r = l.peek() {
|
||||
str += string(r)
|
||||
str.WriteRune(r)
|
||||
l.next()
|
||||
}
|
||||
// break loop if not a dot
|
||||
if r != '.' {
|
||||
break
|
||||
}
|
||||
str += "."
|
||||
str.WriteString(".")
|
||||
// skip trailing whitespace after dot
|
||||
l.next()
|
||||
for r = l.peek(); isSpace(r); r = l.peek() {
|
||||
str += string(r)
|
||||
str.WriteRune(r)
|
||||
l.next()
|
||||
}
|
||||
growingString += str
|
||||
sb.WriteString(str.String())
|
||||
continue
|
||||
} else if r == '.' {
|
||||
// skip
|
||||
} else if !isValidBareChar(r) {
|
||||
return l.errorf("keys cannot contain %c character", r)
|
||||
}
|
||||
growingString += string(r)
|
||||
sb.WriteRune(r)
|
||||
l.next()
|
||||
}
|
||||
l.emitWithValue(tokenKey, growingString)
|
||||
l.emitWithValue(tokenKey, sb.String())
|
||||
return l.lexVoid
|
||||
}
|
||||
|
||||
@@ -383,7 +389,7 @@ func (l *tomlLexer) lexLeftBracket() tomlLexStateFn {
|
||||
}
|
||||
|
||||
func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) {
|
||||
growingString := ""
|
||||
var sb strings.Builder
|
||||
|
||||
if discardLeadingNewLine {
|
||||
if l.follow("\r\n") {
|
||||
@@ -397,14 +403,14 @@ func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNe
|
||||
// find end of string
|
||||
for {
|
||||
if l.follow(terminator) {
|
||||
return growingString, nil
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
next := l.peek()
|
||||
if next == eof {
|
||||
break
|
||||
}
|
||||
growingString += string(l.next())
|
||||
sb.WriteRune(l.next())
|
||||
}
|
||||
|
||||
return "", errors.New("unclosed string")
|
||||
@@ -438,7 +444,7 @@ func (l *tomlLexer) lexLiteralString() tomlLexStateFn {
|
||||
// Terminator is the substring indicating the end of the token.
|
||||
// The resulting string does not include the terminator.
|
||||
func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, acceptNewLines bool) (string, error) {
|
||||
growingString := ""
|
||||
var sb strings.Builder
|
||||
|
||||
if discardLeadingNewLine {
|
||||
if l.follow("\r\n") {
|
||||
@@ -451,7 +457,7 @@ func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine,
|
||||
|
||||
for {
|
||||
if l.follow(terminator) {
|
||||
return growingString, nil
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
if l.follow("\\") {
|
||||
@@ -469,61 +475,61 @@ func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine,
|
||||
l.next()
|
||||
}
|
||||
case '"':
|
||||
growingString += "\""
|
||||
sb.WriteString("\"")
|
||||
l.next()
|
||||
case 'n':
|
||||
growingString += "\n"
|
||||
sb.WriteString("\n")
|
||||
l.next()
|
||||
case 'b':
|
||||
growingString += "\b"
|
||||
sb.WriteString("\b")
|
||||
l.next()
|
||||
case 'f':
|
||||
growingString += "\f"
|
||||
sb.WriteString("\f")
|
||||
l.next()
|
||||
case '/':
|
||||
growingString += "/"
|
||||
sb.WriteString("/")
|
||||
l.next()
|
||||
case 't':
|
||||
growingString += "\t"
|
||||
sb.WriteString("\t")
|
||||
l.next()
|
||||
case 'r':
|
||||
growingString += "\r"
|
||||
sb.WriteString("\r")
|
||||
l.next()
|
||||
case '\\':
|
||||
growingString += "\\"
|
||||
sb.WriteString("\\")
|
||||
l.next()
|
||||
case 'u':
|
||||
l.next()
|
||||
code := ""
|
||||
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 = code + string(c)
|
||||
code.WriteRune(c)
|
||||
}
|
||||
intcode, err := strconv.ParseInt(code, 16, 32)
|
||||
intcode, err := strconv.ParseInt(code.String(), 16, 32)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid unicode escape: \\u" + code)
|
||||
return "", errors.New("invalid unicode escape: \\u" + code.String())
|
||||
}
|
||||
growingString += string(rune(intcode))
|
||||
sb.WriteRune(rune(intcode))
|
||||
case 'U':
|
||||
l.next()
|
||||
code := ""
|
||||
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 = code + string(c)
|
||||
code.WriteRune(c)
|
||||
}
|
||||
intcode, err := strconv.ParseInt(code, 16, 64)
|
||||
intcode, err := strconv.ParseInt(code.String(), 16, 64)
|
||||
if err != nil {
|
||||
return "", errors.New("invalid unicode escape: \\U" + code)
|
||||
return "", errors.New("invalid unicode escape: \\U" + code.String())
|
||||
}
|
||||
growingString += string(rune(intcode))
|
||||
sb.WriteRune(rune(intcode))
|
||||
default:
|
||||
return "", errors.New("invalid escape sequence: \\" + string(l.peek()))
|
||||
}
|
||||
@@ -534,7 +540,7 @@ func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine,
|
||||
return "", fmt.Errorf("unescaped control character %U", r)
|
||||
}
|
||||
l.next()
|
||||
growingString += string(r)
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
|
||||
if l.peek() == eof {
|
||||
@@ -769,19 +775,19 @@ func init() {
|
||||
// /!\ also matches the empty string
|
||||
//
|
||||
// Example matches:
|
||||
//1979-05-27T07:32:00Z
|
||||
//1979-05-27T00:32:00-07:00
|
||||
//1979-05-27T00:32:00.999999-07:00
|
||||
//1979-05-27 07:32:00Z
|
||||
//1979-05-27 00:32:00-07:00
|
||||
//1979-05-27 00:32:00.999999-07:00
|
||||
//1979-05-27T07:32:00
|
||||
//1979-05-27T00:32:00.999999
|
||||
//1979-05-27 07:32:00
|
||||
//1979-05-27 00:32:00.999999
|
||||
//1979-05-27
|
||||
//07:32:00
|
||||
//00:32:00.999999
|
||||
// 1979-05-27T07:32:00Z
|
||||
// 1979-05-27T00:32:00-07:00
|
||||
// 1979-05-27T00:32:00.999999-07:00
|
||||
// 1979-05-27 07:32:00Z
|
||||
// 1979-05-27 00:32:00-07:00
|
||||
// 1979-05-27 00:32:00.999999-07:00
|
||||
// 1979-05-27T07:32:00
|
||||
// 1979-05-27T00:32:00.999999
|
||||
// 1979-05-27 07:32:00
|
||||
// 1979-05-27 00:32:00.999999
|
||||
// 1979-05-27
|
||||
// 07:32:00
|
||||
// 00:32:00.999999
|
||||
dateRegexp = regexp.MustCompile(`^(?:\d{1,4}-\d{2}-\d{2})?(?:[T ]?\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?)?`)
|
||||
}
|
||||
|
||||
|
||||
+32
-3
@@ -76,6 +76,7 @@ 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 {
|
||||
@@ -436,6 +437,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
|
||||
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,
|
||||
@@ -474,6 +476,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er
|
||||
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 {
|
||||
@@ -516,13 +519,13 @@ func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (int
|
||||
|
||||
// Convert given marshal value to toml value
|
||||
func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||
e.line++
|
||||
if mtype.Kind() == reflect.Ptr {
|
||||
switch {
|
||||
case isCustomMarshaler(mtype):
|
||||
return callCustomMarshaler(mval)
|
||||
case isTextMarshaler(mtype):
|
||||
return callTextMarshaler(mval)
|
||||
b, err := callTextMarshaler(mval)
|
||||
return string(b), err
|
||||
default:
|
||||
return e.valueToToml(mtype.Elem(), mval.Elem())
|
||||
}
|
||||
@@ -534,7 +537,8 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface
|
||||
case isCustomMarshaler(mtype):
|
||||
return callCustomMarshaler(mval)
|
||||
case isTextMarshaler(mtype):
|
||||
return callTextMarshaler(mval)
|
||||
b, err := callTextMarshaler(mval)
|
||||
return string(b), err
|
||||
case isTree(mtype):
|
||||
return e.valueToTree(mtype, mval)
|
||||
case isOtherSequence(mtype), isCustomMarshalerSequence(mtype), isTextMarshalerSequence(mtype):
|
||||
@@ -577,6 +581,25 @@ func (e *Encoder) appendTree(t, o *Tree) error {
|
||||
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 {
|
||||
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.
|
||||
@@ -681,6 +704,8 @@ func (d *Decoder) unmarshal(v interface{}) error {
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -717,6 +742,10 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.V
|
||||
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)
|
||||
}
|
||||
|
||||
+155
-27
@@ -14,60 +14,69 @@ import (
|
||||
)
|
||||
|
||||
type basicMarshalTestStruct struct {
|
||||
String string `toml:"Zstring"`
|
||||
StringList []string `toml:"Ystrlist"`
|
||||
Sub basicMarshalTestSubStruct `toml:"Xsubdoc"`
|
||||
SubList []basicMarshalTestSubStruct `toml:"Wsublist"`
|
||||
String string `toml:"Zstring"`
|
||||
StringList []string `toml:"Ystrlist"`
|
||||
BasicMarshalTestSubAnonymousStruct
|
||||
Sub basicMarshalTestSubStruct `toml:"Xsubdoc"`
|
||||
SubList []basicMarshalTestSubStruct `toml:"Wsublist"`
|
||||
}
|
||||
|
||||
type basicMarshalTestSubStruct struct {
|
||||
String2 string
|
||||
}
|
||||
|
||||
var basicTestData = basicMarshalTestStruct{
|
||||
String: "Hello",
|
||||
StringList: []string{"Howdy", "Hey There"},
|
||||
Sub: basicMarshalTestSubStruct{"One"},
|
||||
SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
|
||||
type BasicMarshalTestSubAnonymousStruct struct {
|
||||
String3 string
|
||||
}
|
||||
|
||||
var basicTestToml = []byte(`Ystrlist = ["Howdy", "Hey There"]
|
||||
Zstring = "Hello"
|
||||
var basicTestData = basicMarshalTestStruct{
|
||||
String: "Hello",
|
||||
StringList: []string{"Howdy", "Hey There"},
|
||||
BasicMarshalTestSubAnonymousStruct: BasicMarshalTestSubAnonymousStruct{"One"},
|
||||
Sub: basicMarshalTestSubStruct{"Two"},
|
||||
SubList: []basicMarshalTestSubStruct{{"Three"}, {"Four"}},
|
||||
}
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Two"
|
||||
var basicTestToml = []byte(`String3 = "One"
|
||||
Ystrlist = ["Howdy", "Hey There"]
|
||||
Zstring = "Hello"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Three"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Four"
|
||||
|
||||
[Xsubdoc]
|
||||
String2 = "One"
|
||||
String2 = "Two"
|
||||
`)
|
||||
|
||||
var basicTestTomlCustomIndentation = []byte(`Ystrlist = ["Howdy", "Hey There"]
|
||||
var basicTestTomlCustomIndentation = []byte(`String3 = "One"
|
||||
Ystrlist = ["Howdy", "Hey There"]
|
||||
Zstring = "Hello"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Two"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Three"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Four"
|
||||
|
||||
[Xsubdoc]
|
||||
String2 = "One"
|
||||
String2 = "Two"
|
||||
`)
|
||||
|
||||
var basicTestTomlOrdered = []byte(`Zstring = "Hello"
|
||||
Ystrlist = ["Howdy", "Hey There"]
|
||||
String3 = "One"
|
||||
|
||||
[Xsubdoc]
|
||||
String2 = "One"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Two"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Three"
|
||||
|
||||
[[Wsublist]]
|
||||
String2 = "Four"
|
||||
`)
|
||||
|
||||
var marshalTestToml = []byte(`title = "TOML Marshal Testing"
|
||||
@@ -979,6 +988,40 @@ func TestCustomMarshaler(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type IntOrString string
|
||||
|
||||
func (x *IntOrString) MarshalTOML() ([]byte, error) {
|
||||
s := *(*string)(x)
|
||||
_, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return []byte(fmt.Sprintf(`"%s"`, s)), nil
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
func TestNestedCustomMarshaler(t *testing.T) {
|
||||
num := IntOrString("100")
|
||||
str := IntOrString("hello")
|
||||
var parent = struct {
|
||||
IntField *IntOrString `toml:"int"`
|
||||
StringField *IntOrString `toml:"string"`
|
||||
}{
|
||||
&num,
|
||||
&str,
|
||||
}
|
||||
|
||||
result, err := Marshal(parent)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := `int = 100
|
||||
string = "hello"
|
||||
`
|
||||
if !bytes.Equal(result, []byte(expected)) {
|
||||
t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
type textMarshaler struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
@@ -1079,7 +1122,7 @@ type customPointerMarshaler struct {
|
||||
}
|
||||
|
||||
func (m *customPointerMarshaler) MarshalTOML() ([]byte, error) {
|
||||
return []byte("hidden"), nil
|
||||
return []byte(`"hidden"`), nil
|
||||
}
|
||||
|
||||
type textPointerMarshaler struct {
|
||||
@@ -2151,10 +2194,7 @@ func TestUnmarshalBadDuration(t *testing.T) {
|
||||
result := testBadDuration{}
|
||||
err := NewDecoder(buf).Decode(&result)
|
||||
if err == nil {
|
||||
t.Fatal()
|
||||
}
|
||||
if err.Error() != "(1, 1): Can't convert 1z(string) to time.Duration. time: unknown unit z in duration 1z" {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
t.Fatal("expected bad duration error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3848,3 +3888,91 @@ func TestPreserveNotEmptyField(t *testing.T) {
|
||||
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// github issue 432
|
||||
func TestUnmarshalEmptyInterface(t *testing.T) {
|
||||
doc := []byte(`User = "pelletier"`)
|
||||
|
||||
var v interface{}
|
||||
|
||||
err := Unmarshal(doc, &v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
x, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if x["User"] != "pelletier" {
|
||||
t.Fatalf("expected User=pelletier, but got %v", x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalEmptyInterfaceDeep(t *testing.T) {
|
||||
doc := []byte(`
|
||||
User = "pelletier"
|
||||
Age = 99
|
||||
|
||||
[foo]
|
||||
bar = 42
|
||||
`)
|
||||
|
||||
var v interface{}
|
||||
|
||||
err := Unmarshal(doc, &v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
x, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"User": "pelletier",
|
||||
"Age": 99,
|
||||
"foo": map[string]interface{}{
|
||||
"bar": 42,
|
||||
},
|
||||
}
|
||||
|
||||
reflect.DeepEqual(x, expected)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Key string `toml:"key"`
|
||||
Obj Custom `toml:"obj"`
|
||||
}
|
||||
|
||||
type Custom struct {
|
||||
v string
|
||||
}
|
||||
|
||||
func (c *Custom) UnmarshalTOML(v interface{}) error {
|
||||
c.v = "called"
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestGithubIssue431(t *testing.T) {
|
||||
doc := `key = "value"`
|
||||
tree, err := LoadBytes([]byte(doc))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
var c Config
|
||||
if err := tree.Unmarshal(&c); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if c.Key != "value" {
|
||||
t.Errorf("expected c.Key='value', not '%s'", c.Key)
|
||||
}
|
||||
|
||||
if c.Obj.v == "called" {
|
||||
t.Errorf("UnmarshalTOML should not have been called")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,89 @@ func (t *Tree) GetPath(keys []string) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 == "" {
|
||||
@@ -130,6 +213,50 @@ func (t *Tree) GetPosition(key string) 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 {
|
||||
@@ -212,7 +339,8 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac
|
||||
// go to most recent element
|
||||
if len(node) == 0 {
|
||||
// create element if it does not exist
|
||||
subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
|
||||
node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col}))
|
||||
subtree.values[intermediateKey] = node
|
||||
}
|
||||
subtree = node[len(node)-1]
|
||||
}
|
||||
@@ -232,6 +360,8 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac
|
||||
toInsert = value
|
||||
case *tomlValue:
|
||||
v.comment = opts.Comment
|
||||
v.commented = opts.Commented
|
||||
v.multiline = opts.Multiline
|
||||
toInsert = v
|
||||
default:
|
||||
toInsert = &tomlValue{value: value,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,41 @@ func TestTomlGet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTomlGetArray(t *testing.T) {
|
||||
tree, _ := Load(`
|
||||
[test]
|
||||
key = ["one", "two"]
|
||||
key2 = [true, false, false]
|
||||
key3 = [1.5,2.5]
|
||||
`)
|
||||
|
||||
if tree.GetArray("") != tree {
|
||||
t.Errorf("GetArray should return the tree itself when given an empty path")
|
||||
}
|
||||
|
||||
expect := []string{"one", "two"}
|
||||
actual := tree.GetArray("test.key").([]string)
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("GetArray should return the []string value")
|
||||
}
|
||||
|
||||
expect2 := []bool{true, false, false}
|
||||
actual2 := tree.GetArray("test.key2").([]bool)
|
||||
if !reflect.DeepEqual(actual2, expect2) {
|
||||
t.Errorf("GetArray should return the []bool value")
|
||||
}
|
||||
|
||||
expect3 := []float64{1.5, 2.5}
|
||||
actual3 := tree.GetArray("test.key3").([]float64)
|
||||
if !reflect.DeepEqual(actual3, expect3) {
|
||||
t.Errorf("GetArray should return the []float64 value")
|
||||
}
|
||||
|
||||
if tree.GetArray(`\`) != nil {
|
||||
t.Errorf("should return nil when the key is malformed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTomlGetDefault(t *testing.T) {
|
||||
tree, _ := Load(`
|
||||
[test]
|
||||
@@ -148,6 +184,51 @@ func TestTomlGetPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTomlGetArrayPath(t *testing.T) {
|
||||
for idx, item := range []struct {
|
||||
Name string
|
||||
Path []string
|
||||
Make func() (tree *Tree, expected interface{})
|
||||
}{
|
||||
{
|
||||
Name: "empty",
|
||||
Path: []string{},
|
||||
Make: func() (tree *Tree, expected interface{}) {
|
||||
tree = newTree()
|
||||
expected = tree
|
||||
return
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "int64",
|
||||
Path: []string{"a"},
|
||||
Make: func() (tree *Tree, expected interface{}) {
|
||||
var err error
|
||||
tree, err = Load(`a = [1,2,3]`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expected = []int64{1, 2, 3}
|
||||
return
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(item.Name, func(t *testing.T) {
|
||||
tree, expected := item.Make()
|
||||
result := tree.GetArrayPath(item.Path)
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("GetArrayPath[%d] %v - expected %#v, got %#v instead.", idx, item.Path, expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
tree, _ := Load("[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6")
|
||||
if tree.GetArrayPath([]string{"whatever"}) != nil {
|
||||
t.Error("GetArrayPath should return nil when the key does not exist")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTomlFromMap(t *testing.T) {
|
||||
simpleMap := map[string]interface{}{"hello": 42}
|
||||
tree, err := TreeFromMap(simpleMap)
|
||||
|
||||
@@ -57,6 +57,19 @@ func simpleValueCoercion(object interface{}) (interface{}, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -124,3 +125,119 @@ func TestRoundTripArrayOfTables(t *testing.T) {
|
||||
t.Errorf("want:\n%s\ngot:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTomlSliceOfSlice(t *testing.T) {
|
||||
tree, err := Load(` hosts=[["10.1.0.107:9092","10.1.0.107:9093", "192.168.0.40:9094"] ] `)
|
||||
m := tree.ToMap()
|
||||
tree, err = TreeFromMap(m)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
type Struct struct {
|
||||
Hosts [][]string
|
||||
}
|
||||
var actual Struct
|
||||
tree.Unmarshal(&actual)
|
||||
|
||||
expected := Struct{Hosts: [][]string{[]string{"10.1.0.107:9092", "10.1.0.107:9093", "192.168.0.40:9094"}}}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTomlSliceOfSliceOfSlice(t *testing.T) {
|
||||
tree, err := Load(` hosts=[[["10.1.0.107:9092","10.1.0.107:9093", "192.168.0.40:9094"] ]] `)
|
||||
m := tree.ToMap()
|
||||
tree, err = TreeFromMap(m)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
type Struct struct {
|
||||
Hosts [][][]string
|
||||
}
|
||||
var actual Struct
|
||||
tree.Unmarshal(&actual)
|
||||
|
||||
expected := Struct{Hosts: [][][]string{[][]string{[]string{"10.1.0.107:9092", "10.1.0.107:9093", "192.168.0.40:9094"}}}}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTomlSliceOfSliceInt(t *testing.T) {
|
||||
tree, err := Load(` hosts=[[1,2,3],[4,5,6] ] `)
|
||||
m := tree.ToMap()
|
||||
tree, err = TreeFromMap(m)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
type Struct struct {
|
||||
Hosts [][]int
|
||||
}
|
||||
var actual Struct
|
||||
err = tree.Unmarshal(&actual)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
|
||||
expected := Struct{Hosts: [][]int{[]int{1, 2, 3}, []int{4, 5, 6}}}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
func TestTomlSliceOfSliceInt64(t *testing.T) {
|
||||
tree, err := Load(` hosts=[[1,2,3],[4,5,6] ] `)
|
||||
m := tree.ToMap()
|
||||
tree, err = TreeFromMap(m)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
type Struct struct {
|
||||
Hosts [][]int64
|
||||
}
|
||||
var actual Struct
|
||||
err = tree.Unmarshal(&actual)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
|
||||
expected := Struct{Hosts: [][]int64{[]int64{1, 2, 3}, []int64{4, 5, 6}}}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTomlSliceOfSliceInt64FromMap(t *testing.T) {
|
||||
tree, err := TreeFromMap(map[string]interface{}{"hosts": [][]interface{}{[]interface{}{int32(1), int8(2), 3}}})
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
type Struct struct {
|
||||
Hosts [][]int64
|
||||
}
|
||||
var actual Struct
|
||||
err = tree.Unmarshal(&actual)
|
||||
if err != nil {
|
||||
t.Error("should not error", err)
|
||||
}
|
||||
|
||||
expected := Struct{Hosts: [][]int64{[]int64{1, 2, 3}}}
|
||||
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
func TestTomlSliceOfSliceError(t *testing.T) { // make Codecov happy
|
||||
_, err := TreeFromMap(map[string]interface{}{"hosts": [][]interface{}{[]interface{}{1, 2, []struct{}{}}}})
|
||||
expected := "cannot convert type []struct {} to Tree"
|
||||
if err.Error() != expected {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ func tomlValueStringRepresentation(v interface{}, commented string, indent strin
|
||||
return "\"" + encodeTomlString(value) + "\"", nil
|
||||
case []byte:
|
||||
b, _ := v.([]byte)
|
||||
return tomlValueStringRepresentation(string(b), commented, indent, ord, arraysOneElementPerLine)
|
||||
return string(b), nil
|
||||
case bool:
|
||||
if value {
|
||||
return "true", nil
|
||||
|
||||
Reference in New Issue
Block a user