golangci-lint: misc (#529)

This commit is contained in:
Vincent Serpoul
2021-04-28 08:29:00 +08:00
committed by GitHub
parent 1e80267558
commit 201d5dd422
6 changed files with 235 additions and 84 deletions
+2
View File
@@ -566,6 +566,7 @@ func fieldBoolTag(field reflect.StructField, tag string) bool {
return ok && x == "true" return ok && x == "true"
} }
//nolint:cyclop
func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) { func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) {
var err error var err error
@@ -580,6 +581,7 @@ func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
if enc.indentTables && len(ctx.parentKey) > 0 { if enc.indentTables && len(ctx.parentKey) > 0 {
ctx.indent++ ctx.indent++
} }
+6
View File
@@ -394,7 +394,10 @@ func equalStringsIgnoreNewlines(t *testing.T, expected string, actual string) {
assert.Equal(t, strings.Trim(expected, cutset), strings.Trim(actual, cutset)) assert.Equal(t, strings.Trim(expected, cutset), strings.Trim(actual, cutset))
} }
//nolint:funlen
func TestMarshalIndentTables(t *testing.T) { func TestMarshalIndentTables(t *testing.T) {
t.Parallel()
examples := []struct { examples := []struct {
desc string desc string
v interface{} v interface{}
@@ -443,7 +446,10 @@ root = 'value0'
} }
for _, e := range examples { for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) { t.Run(e.desc, func(t *testing.T) {
t.Parallel()
var buf strings.Builder var buf strings.Builder
enc := toml.NewEncoder(&buf) enc := toml.NewEncoder(&buf)
enc.SetIndentTables(true) enc.SetIndentTables(true)
+5 -8
View File
@@ -98,9 +98,9 @@ func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) {
} }
if b[0] == '#' { if b[0] == '#' {
_, rest, err := scanComment(b) _, rest := scanComment(b)
return ref, rest, err return ref, rest, nil
} }
if b[0] == '\n' || b[0] == '\r' { if b[0] == '\n' || b[0] == '\r' {
@@ -121,9 +121,9 @@ func (p *parser) parseExpression(b []byte) (ast.Reference, []byte, error) {
b = p.parseWhitespace(b) b = p.parseWhitespace(b)
if len(b) > 0 && b[0] == '#' { if len(b) > 0 && b[0] == '#' {
_, rest, err := scanComment(b) _, rest := scanComment(b)
return ref, rest, err return ref, rest, nil
} }
return ref, b, nil return ref, b, nil
@@ -456,10 +456,7 @@ func (p *parser) parseOptionalWhitespaceCommentNewline(b []byte) ([]byte, error)
b = p.parseWhitespace(b) b = p.parseWhitespace(b)
if len(b) > 0 && b[0] == '#' { if len(b) > 0 && b[0] == '#' {
_, b, err = scanComment(b) _, b = scanComment(b)
if err != nil {
return nil, err
}
} }
if len(b) == 0 { if len(b) == 0 {
+53 -39
View File
@@ -1,9 +1,12 @@
package toml package toml
import "fmt" import (
"errors"
)
func scanFollows(b []byte, pattern string) bool { func scanFollows(b []byte, pattern string) bool {
n := len(pattern) n := len(pattern)
return len(b) >= n && string(b[:n]) == pattern return len(b) >= n && string(b[:n]) == pattern
} }
@@ -38,6 +41,7 @@ func scanUnquotedKey(b []byte) ([]byte, []byte, error) {
return b[:i], b[i:], nil return b[:i], b[i:], nil
} }
} }
return b, b[len(b):], nil return b, b[len(b):], nil
} }
@@ -57,38 +61,44 @@ func scanLiteralString(b []byte) ([]byte, []byte, error) {
return nil, nil, newDecodeError(b[i:i+1], "literal strings cannot have new lines") return nil, nil, newDecodeError(b[i:i+1], "literal strings cannot have new lines")
} }
} }
return nil, nil, newDecodeError(b[len(b):], "unterminated literal string") return nil, nil, newDecodeError(b[len(b):], "unterminated literal string")
} }
func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) {
//ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body // ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
//ml-literal-string-delim // ml-literal-string-delim
//ml-literal-string-delim = 3apostrophe // ml-literal-string-delim = 3apostrophe
//ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] // ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ]
// //
//mll-content = mll-char / newline // mll-content = mll-char / newline
//mll-char = %x09 / %x20-26 / %x28-7E / non-ascii // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
//mll-quotes = 1*2apostrophe // mll-quotes = 1*2apostrophe
for i := 3; i < len(b); i++ { for i := 3; i < len(b); i++ {
switch b[i] { if b[i] == '\'' && scanFollowsMultilineLiteralStringDelimiter(b[i:]) {
case '\'': return b[:i+3], b[i+3:], nil
if scanFollowsMultilineLiteralStringDelimiter(b[i:]) {
return b[:i+3], b[i+3:], nil
}
} }
} }
return nil, nil, newDecodeError(b[len(b):], `multiline literal string not terminated by '''`) return nil, nil, newDecodeError(b[len(b):], `multiline literal string not terminated by '''`)
} }
var (
errWindowsNewLineMissing = errors.New(`windows new line missing \n`)
errWindowsNewLineCRLF = errors.New(`windows new line should be \r\n`)
)
func scanWindowsNewline(b []byte) ([]byte, []byte, error) { func scanWindowsNewline(b []byte) ([]byte, []byte, error) {
if len(b) < 2 { const lenLF = 2
return nil, nil, fmt.Errorf(`windows new line missing \n`) if len(b) < lenLF {
return nil, nil, errWindowsNewLineMissing
} }
if b[1] != '\n' { if b[1] != '\n' {
return nil, nil, fmt.Errorf(`windows new line should be \r\n`) return nil, nil, errWindowsNewLineCRLF
} }
return b[:2], b[2:], nil
return b[:lenLF], b[lenLF:], nil
} }
func scanWhitespace(b []byte) ([]byte, []byte) { func scanWhitespace(b []byte) ([]byte, []byte) {
@@ -100,27 +110,31 @@ func scanWhitespace(b []byte) ([]byte, []byte) {
return b[:i], b[i:] return b[:i], b[i:]
} }
} }
return b, b[len(b):] return b, b[len(b):]
} }
func scanComment(b []byte) ([]byte, []byte, error) { //nolint:unparam
//;; Comment func scanComment(b []byte) ([]byte, []byte) {
// ;; Comment
// //
//comment-start-symbol = %x23 ; # // comment-start-symbol = %x23 ; #
//non-ascii = %x80-D7FF / %xE000-10FFFF // non-ascii = %x80-D7FF / %xE000-10FFFF
//non-eol = %x09 / %x20-7F / non-ascii // non-eol = %x09 / %x20-7F / non-ascii
// //
//comment = comment-start-symbol *non-eol // comment = comment-start-symbol *non-eol
for i := 1; i < len(b); i++ { for i := 1; i < len(b); i++ {
switch b[i] { if b[i] == '\n' {
case '\n': return b[:i], b[i:]
return b[:i], b[i:], nil
} }
} }
return b, nil, nil
return b, nil
} }
var errBasicLineNotTerminatedByQuote = errors.New(`basic string not terminated by "`)
//nolint:godox
// TODO perform validation on the string? // TODO perform validation on the string?
func scanBasicString(b []byte) ([]byte, []byte, error) { func scanBasicString(b []byte) ([]byte, []byte, error) {
// basic-string = quotation-mark *basic-char quotation-mark // basic-string = quotation-mark *basic-char quotation-mark
@@ -142,22 +156,22 @@ func scanBasicString(b []byte) ([]byte, []byte, error) {
} }
} }
return nil, nil, fmt.Errorf(`basic string not terminated by "`) return nil, nil, errBasicLineNotTerminatedByQuote
} }
//nolint:godox
// TODO perform validation on the string? // TODO perform validation on the string?
func scanMultilineBasicString(b []byte) ([]byte, []byte, error) { func scanMultilineBasicString(b []byte) ([]byte, []byte, error) {
//ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body
//ml-basic-string-delim // ml-basic-string-delim
//ml-basic-string-delim = 3quotation-mark // ml-basic-string-delim = 3quotation-mark
//ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]
// //
//mlb-content = mlb-char / newline / mlb-escaped-nl // mlb-content = mlb-char / newline / mlb-escaped-nl
//mlb-char = mlb-unescaped / escaped // mlb-char = mlb-unescaped / escaped
//mlb-quotes = 1*2quotation-mark // mlb-quotes = 1*2quotation-mark
//mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
//mlb-escaped-nl = escape ws newline *( wschar / newline ) // mlb-escaped-nl = escape ws newline *( wschar / newline )
for i := 3; i < len(b); i++ { for i := 3; i < len(b); i++ {
switch b[i] { switch b[i] {
case '"': case '"':
+84 -23
View File
@@ -2,6 +2,7 @@ package toml
import ( import (
"encoding" "encoding"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@@ -17,6 +18,7 @@ func Unmarshal(data []byte, v interface{}) error {
p := parser{} p := parser{}
p.Reset(data) p.Reset(data)
d := decoder{} d := decoder{}
return d.FromParser(&p, v) return d.FromParser(&p, v)
} }
@@ -54,8 +56,9 @@ func (d *Decoder) SetStrict(strict bool) {
func (d *Decoder) Decode(v interface{}) error { func (d *Decoder) Decode(v interface{}) error {
b, err := ioutil.ReadAll(d.r) b, err := ioutil.ReadAll(d.r)
if err != nil { if err != nil {
return err return fmt.Errorf("Decode: %w", err)
} }
p := parser{} p := parser{}
p.Reset(b) p.Reset(b)
dec := decoder{ dec := decoder{
@@ -63,6 +66,7 @@ func (d *Decoder) Decode(v interface{}) error {
Enabled: d.strict, Enabled: d.strict,
}, },
} }
return dec.FromParser(&p, v) return dec.FromParser(&p, v)
} }
@@ -90,19 +94,19 @@ func (d *decoder) arrayIndex(append bool, v reflect.Value) int {
idx++ idx++
d.arrayIndexes[v] = idx d.arrayIndexes[v] = idx
} }
return idx return idx
} }
func (d *decoder) FromParser(p *parser, v interface{}) error { func (d *decoder) FromParser(p *parser, v interface{}) error {
err := d.fromParser(p, v) err := d.fromParser(p, v)
if err != nil {
de, ok := err.(*decodeError)
if ok {
err = wrapDecodeError(p.data, de)
}
}
if err == nil { if err == nil {
err = d.strict.Error(p.data) return d.strict.Error(p.data)
}
var e *decodeError
if errors.As(err, &e) {
return wrapDecodeError(p.data, e)
} }
return err return err
@@ -110,29 +114,43 @@ func (d *decoder) FromParser(p *parser, v interface{}) error {
func keyLocation(node ast.Node) []byte { func keyLocation(node ast.Node) []byte {
k := node.Key() k := node.Key()
hasOne := k.Next() hasOne := k.Next()
if !hasOne { if !hasOne {
panic("should not be called with empty key") panic("should not be called with empty key")
} }
start := k.Node().Data start := k.Node().Data
end := k.Node().Data end := k.Node().Data
for k.Next() { for k.Next() {
end = k.Node().Data end = k.Node().Data
} }
return unsafe.BytesRange(start, end) return unsafe.BytesRange(start, end)
} }
var (
errFromParserExpectingPointer = errors.New("expecting a pointer as target")
errFromParserExpectingNonNilPointer = errors.New("expecting non nil pointer as target")
)
//nolint:funlen,cyclop
func (d *decoder) fromParser(p *parser, v interface{}) error { func (d *decoder) fromParser(p *parser, v interface{}) error {
r := reflect.ValueOf(v) r := reflect.ValueOf(v)
if r.Kind() != reflect.Ptr { if r.Kind() != reflect.Ptr {
return fmt.Errorf("need to target a pointer, not %s", r.Kind()) return fmt.Errorf("fromParser: %w, not %s", errFromParserExpectingPointer, r.Kind())
}
if r.IsNil() {
return fmt.Errorf("target pointer must be non-nil")
} }
var skipUntilTable bool if r.IsNil() {
var root target = valueTarget(r.Elem()) return errFromParserExpectingNonNilPointer
}
var (
skipUntilTable bool
root target = valueTarget(r.Elem())
)
current := root current := root
for p.NextExpression() { for p.NextExpression() {
@@ -144,10 +162,11 @@ func (d *decoder) fromParser(p *parser, v interface{}) error {
err := d.seen.CheckExpression(node) err := d.seen.CheckExpression(node)
if err != nil { if err != nil {
return err return fmt.Errorf("fromParser: %w", err)
} }
var found bool var found bool
switch node.Kind { switch node.Kind {
case ast.KeyValue: case ast.KeyValue:
err = d.unmarshalKeyValue(current, node) err = d.unmarshalKeyValue(current, node)
@@ -167,7 +186,7 @@ func (d *decoder) fromParser(p *parser, v interface{}) error {
d.strict.EnterArrayTable(node) d.strict.EnterArrayTable(node)
current, found, err = d.scopeWithArrayTable(root, node.Key()) current, found, err = d.scopeWithArrayTable(root, node.Key())
default: default:
panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) panic(fmt.Sprintf("fromParser: this should not be a top level node type: %s", node.Kind))
} }
if err != nil { if err != nil {
@@ -176,6 +195,7 @@ func (d *decoder) fromParser(p *parser, v interface{}) error {
if !found { if !found {
skipUntilTable = true skipUntilTable = true
d.strict.MissingTable(node) d.strict.MissingTable(node)
} }
} }
@@ -192,38 +212,49 @@ func (d *decoder) fromParser(p *parser, v interface{}) error {
// When encountering slices, it should always use its last element, and error // When encountering slices, it should always use its last element, and error
// if the slice does not have any. // if the slice does not have any.
func (d *decoder) scopeWithKey(x target, key ast.Iterator) (target, bool, error) { func (d *decoder) scopeWithKey(x target, key ast.Iterator) (target, bool, error) {
var err error var (
found := true err error
found bool
)
for key.Next() { for key.Next() {
n := key.Node() n := key.Node()
x, found, err = d.scopeTableTarget(false, x, string(n.Data)) x, found, err = d.scopeTableTarget(false, x, string(n.Data))
if err != nil || !found { if err != nil || !found {
return nil, found, err return nil, found, err
} }
} }
return x, true, nil return x, true, nil
} }
//nolint:cyclop
// scopeWithArrayTable performs target scoping when unmarshaling an // scopeWithArrayTable performs target scoping when unmarshaling an
// ast.ArrayTable node. // ast.ArrayTable node.
// //
// It is the same as scopeWithKey, but when scoping the last part of the key // It is the same as scopeWithKey, but when scoping the last part of the key
// it creates a new element in the array instead of using the last one. // it creates a new element in the array instead of using the last one.
func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool, error) { func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool, error) {
var err error var (
found := true err error
found bool
)
for key.Next() { for key.Next() {
n := key.Node() n := key.Node()
if !n.Next().Valid() { // want to stop at one before last if !n.Next().Valid() { // want to stop at one before last
break break
} }
x, found, err = d.scopeTableTarget(false, x, string(n.Data)) x, found, err = d.scopeTableTarget(false, x, string(n.Data))
if err != nil || !found { if err != nil || !found {
return nil, found, err return nil, found, err
} }
} }
n := key.Node() n := key.Node()
x, found, err = d.scopeTableTarget(false, x, string(n.Data)) x, found, err = d.scopeTableTarget(false, x, string(n.Data))
if err != nil || !found { if err != nil || !found {
return x, found, err return x, found, err
@@ -236,6 +267,7 @@ func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool,
if err != nil { if err != nil {
return x, false, err return x, false, err
} }
v = x.get() v = x.get()
} }
@@ -244,6 +276,7 @@ func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool,
if err != nil { if err != nil {
return x, found, err return x, found, err
} }
v = x.get() v = x.get()
} }
@@ -252,6 +285,7 @@ func (d *decoder) scopeWithArrayTable(x target, key ast.Iterator) (target, bool,
x, err = scopeSlice(true, x) x, err = scopeSlice(true, x)
case reflect.Array: case reflect.Array:
x, err = d.scopeArray(true, x) x, err = d.scopeArray(true, x)
default:
} }
return x, found, err return x, found, err
@@ -295,22 +329,28 @@ func tryTextUnmarshaler(x target, node ast.Node) (bool, error) {
if v.Type().Implements(textUnmarshalerType) { if v.Type().Implements(textUnmarshalerType) {
return true, v.Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) return true, v.Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
} }
if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) {
return true, v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) return true, v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data)
} }
return false, nil return false, nil
} }
//nolint:cyclop
func (d *decoder) unmarshalValue(x target, node ast.Node) error { func (d *decoder) unmarshalValue(x target, node ast.Node) error {
v := x.get() v := x.get()
if v.Kind() == reflect.Ptr { if v.Kind() == reflect.Ptr {
if !v.Elem().IsValid() { if !v.Elem().IsValid() {
err := x.set(reflect.New(v.Type().Elem())) err := x.set(reflect.New(v.Type().Elem()))
if err != nil { if err != nil {
return err return err
} }
v = x.get() v = x.get()
} }
return d.unmarshalValue(valueTarget(v.Elem()), node) return d.unmarshalValue(valueTarget(v.Elem()), node)
} }
@@ -339,37 +379,44 @@ func (d *decoder) unmarshalValue(x target, node ast.Node) error {
case ast.LocalDate: case ast.LocalDate:
return unmarshalLocalDate(x, node) return unmarshalLocalDate(x, node)
default: default:
panic(fmt.Errorf("unhandled unmarshalValue kind %s", node.Kind)) panic(fmt.Sprintf("unmarshalValue: unhandled unmarshalValue kind %s", node.Kind))
} }
} }
func unmarshalLocalDate(x target, node ast.Node) error { func unmarshalLocalDate(x target, node ast.Node) error {
assertNode(ast.LocalDate, node) assertNode(ast.LocalDate, node)
v, err := parseLocalDate(node.Data) v, err := parseLocalDate(node.Data)
if err != nil { if err != nil {
return err return err
} }
return setDate(x, v) return setDate(x, v)
} }
func unmarshalLocalDateTime(x target, node ast.Node) error { func unmarshalLocalDateTime(x target, node ast.Node) error {
assertNode(ast.LocalDateTime, node) assertNode(ast.LocalDateTime, node)
v, rest, err := parseLocalDateTime(node.Data) v, rest, err := parseLocalDateTime(node.Data)
if err != nil { if err != nil {
return err return err
} }
if len(rest) > 0 { if len(rest) > 0 {
return newDecodeError(rest, "extra characters at the end of a local date time") return newDecodeError(rest, "extra characters at the end of a local date time")
} }
return setLocalDateTime(x, v) return setLocalDateTime(x, v)
} }
func unmarshalDateTime(x target, node ast.Node) error { func unmarshalDateTime(x target, node ast.Node) error {
assertNode(ast.DateTime, node) assertNode(ast.DateTime, node)
v, err := parseDateTime(node.Data) v, err := parseDateTime(node.Data)
if err != nil { if err != nil {
return err return err
} }
return setDateTime(x, v) return setDateTime(x, v)
} }
@@ -378,6 +425,7 @@ func setLocalDateTime(x target, v LocalDateTime) error {
cast := v.In(time.Local) cast := v.In(time.Local)
return setDateTime(x, cast) return setDateTime(x, cast)
} }
return x.set(reflect.ValueOf(v)) return x.set(reflect.ValueOf(v))
} }
@@ -398,21 +446,25 @@ func setDate(x target, v LocalDate) error {
func unmarshalString(x target, node ast.Node) error { func unmarshalString(x target, node ast.Node) error {
assertNode(ast.String, node) assertNode(ast.String, node)
return setString(x, string(node.Data)) return setString(x, string(node.Data))
} }
func unmarshalBool(x target, node ast.Node) error { func unmarshalBool(x target, node ast.Node) error {
assertNode(ast.Bool, node) assertNode(ast.Bool, node)
v := node.Data[0] == 't' v := node.Data[0] == 't'
return setBool(x, v) return setBool(x, v)
} }
func unmarshalInteger(x target, node ast.Node) error { func unmarshalInteger(x target, node ast.Node) error {
assertNode(ast.Integer, node) assertNode(ast.Integer, node)
v, err := parseInteger(node.Data) v, err := parseInteger(node.Data)
if err != nil { if err != nil {
return err return err
} }
return setInt64(x, v) return setInt64(x, v)
} }
@@ -422,6 +474,7 @@ func unmarshalFloat(x target, node ast.Node) error {
if err != nil { if err != nil {
return err return err
} }
return setFloat64(x, v) return setFloat64(x, v)
} }
@@ -433,11 +486,13 @@ func (d *decoder) unmarshalInlineTable(x target, node ast.Node) error {
it := node.Children() it := node.Children()
for it.Next() { for it.Next() {
n := it.Node() n := it.Node()
err := d.unmarshalKeyValue(x, n) err := d.unmarshalKeyValue(x, n)
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }
@@ -449,30 +504,36 @@ func (d *decoder) unmarshalArray(x target, node ast.Node) error {
return err return err
} }
it := node.Children()
idx := 0 idx := 0
it := node.Children()
for it.Next() { for it.Next() {
n := it.Node() n := it.Node()
v, err := elementAt(x, idx) v, err := elementAt(x, idx)
if err != nil { if err != nil {
return err return err
} }
if v == nil { if v == nil {
// when we go out of bound for an array just stop processing it to // when we go out of bound for an array just stop processing it to
// mimic encoding/json // mimic encoding/json
break break
} }
err = d.unmarshalValue(v, n) err = d.unmarshalValue(v, n)
if err != nil { if err != nil {
return err return err
} }
idx++ idx++
} }
return nil return nil
} }
func assertNode(expected ast.Kind, node ast.Node) { func assertNode(expected ast.Kind, node ast.Node) {
if node.Kind != expected { if node.Kind != expected {
panic(fmt.Errorf("expected node of kind %s, not %s", expected, node.Kind)) panic(fmt.Sprintf("expected node of kind %s, not %s", expected, node.Kind))
} }
} }
+85 -14
View File
@@ -1,6 +1,7 @@
package toml_test package toml_test
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"strconv" "strconv"
@@ -14,6 +15,8 @@ import (
) )
func TestUnmarshal_Integers(t *testing.T) { func TestUnmarshal_Integers(t *testing.T) {
t.Parallel()
examples := []struct { examples := []struct {
desc string desc string
input string input string
@@ -62,7 +65,10 @@ func TestUnmarshal_Integers(t *testing.T) {
} }
for _, e := range examples { for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) { t.Run(e.desc, func(t *testing.T) {
t.Parallel()
doc := doc{} doc := doc{}
err := toml.Unmarshal([]byte(`A = `+e.input), &doc) err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
require.NoError(t, err) require.NoError(t, err)
@@ -71,7 +77,10 @@ func TestUnmarshal_Integers(t *testing.T) {
} }
} }
//nolint:funlen
func TestUnmarshal_Floats(t *testing.T) { func TestUnmarshal_Floats(t *testing.T) {
t.Parallel()
examples := []struct { examples := []struct {
desc string desc string
input string input string
@@ -161,7 +170,10 @@ func TestUnmarshal_Floats(t *testing.T) {
} }
for _, e := range examples { for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) { t.Run(e.desc, func(t *testing.T) {
t.Parallel()
doc := doc{} doc := doc{}
err := toml.Unmarshal([]byte(`A = `+e.input), &doc) err := toml.Unmarshal([]byte(`A = `+e.input), &doc)
require.NoError(t, err) require.NoError(t, err)
@@ -174,7 +186,10 @@ func TestUnmarshal_Floats(t *testing.T) {
} }
} }
//nolint:funlen
func TestUnmarshal(t *testing.T) { func TestUnmarshal(t *testing.T) {
t.Parallel()
type test struct { type test struct {
target interface{} target interface{}
expected interface{} expected interface{}
@@ -193,6 +208,7 @@ func TestUnmarshal(t *testing.T) {
type doc struct { type doc struct {
A string A string
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{A: "foo"}, expected: &doc{A: "foo"},
@@ -205,6 +221,7 @@ func TestUnmarshal(t *testing.T) {
fruit . flavor = "banana"`, fruit . flavor = "banana"`,
gen: func() test { gen: func() test {
m := map[string]interface{}{} m := map[string]interface{}{}
return test{ return test{
target: &m, target: &m,
expected: &map[string]interface{}{ expected: &map[string]interface{}{
@@ -222,6 +239,7 @@ func TestUnmarshal(t *testing.T) {
"\"b\"" = 2`, "\"b\"" = 2`,
gen: func() test { gen: func() test {
m := map[string]interface{}{} m := map[string]interface{}{}
return test{ return test{
target: &m, target: &m,
expected: &map[string]interface{}{ expected: &map[string]interface{}{
@@ -239,6 +257,7 @@ func TestUnmarshal(t *testing.T) {
type doc struct { type doc struct {
A string A string
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{A: "Test"}, expected: &doc{A: "Test"},
@@ -252,6 +271,7 @@ func TestUnmarshal(t *testing.T) {
type doc struct { type doc struct {
A bool A bool
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{A: true}, expected: &doc{A: true},
@@ -265,6 +285,7 @@ func TestUnmarshal(t *testing.T) {
type doc struct { type doc struct {
A bool A bool
} }
return test{ return test{
target: &doc{A: true}, target: &doc{A: true},
expected: &doc{A: false}, expected: &doc{A: false},
@@ -278,6 +299,7 @@ func TestUnmarshal(t *testing.T) {
type doc struct { type doc struct {
A []string A []string
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{A: []string{"foo", "bar"}}, expected: &doc{A: []string{"foo", "bar"}},
@@ -295,6 +317,7 @@ B = "data"`,
type doc struct { type doc struct {
A A A A
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{A: A{B: "data"}}, expected: &doc{A: A{B: "data"}},
@@ -306,6 +329,7 @@ B = "data"`,
input: `[A]`, input: `[A]`,
gen: func() test { gen: func() test {
var v map[string]interface{} var v map[string]interface{}
return test{ return test{
target: &v, target: &v,
expected: &map[string]interface{}{`A`: map[string]interface{}{}}, expected: &map[string]interface{}{`A`: map[string]interface{}{}},
@@ -323,6 +347,7 @@ B = "data"`,
type doc struct { type doc struct {
Name name Name name
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{Name: name{ expected: &doc{Name: name{
@@ -337,6 +362,7 @@ B = "data"`,
input: `A = {}`, input: `A = {}`,
gen: func() test { gen: func() test {
var v map[string]interface{} var v map[string]interface{}
return test{ return test{
target: &v, target: &v,
expected: &map[string]interface{}{`A`: map[string]interface{}{}}, expected: &map[string]interface{}{`A`: map[string]interface{}{}},
@@ -354,6 +380,7 @@ B = "data"`,
type doc struct { type doc struct {
Names []name Names []name
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{ expected: &doc{
@@ -376,6 +403,7 @@ B = "data"`,
input: `A = "foo"`, input: `A = "foo"`,
gen: func() test { gen: func() test {
doc := map[string]interface{}{} doc := map[string]interface{}{}
return test{ return test{
target: &doc, target: &doc,
expected: &map[string]interface{}{ expected: &map[string]interface{}{
@@ -390,6 +418,7 @@ B = "data"`,
B = 42`, B = 42`,
gen: func() test { gen: func() test {
doc := map[string]interface{}{} doc := map[string]interface{}{}
return test{ return test{
target: &doc, target: &doc,
expected: &map[string]interface{}{ expected: &map[string]interface{}{
@@ -404,6 +433,7 @@ B = "data"`,
input: `A = ["foo", "bar"]`, input: `A = ["foo", "bar"]`,
gen: func() test { gen: func() test {
doc := map[string]interface{}{} doc := map[string]interface{}{}
return test{ return test{
target: &doc, target: &doc,
expected: &map[string]interface{}{ expected: &map[string]interface{}{
@@ -417,6 +447,7 @@ B = "data"`,
input: `A = "foo"`, input: `A = "foo"`,
gen: func() test { gen: func() test {
doc := map[string]string{} doc := map[string]string{}
return test{ return test{
target: &doc, target: &doc,
expected: &map[string]string{ expected: &map[string]string{
@@ -430,6 +461,7 @@ B = "data"`,
input: `A = 42.0`, input: `A = 42.0`,
gen: func() test { gen: func() test {
doc := map[string]string{} doc := map[string]string{}
return test{ return test{
target: &doc, target: &doc,
err: true, err: true,
@@ -447,6 +479,7 @@ B = "data"`,
type Doc struct { type Doc struct {
First []First First []First
} }
return test{ return test{
target: &Doc{}, target: &Doc{},
expected: &Doc{ expected: &Doc{
@@ -464,13 +497,13 @@ B = "data"`,
input: `[[Products]] input: `[[Products]]
Name = "Hammer" Name = "Hammer"
Sku = 738594937 Sku = 738594937
[[Products]] # empty table within the array [[Products]] # empty table within the array
[[Products]] [[Products]]
Name = "Nail" Name = "Nail"
Sku = 284758393 Sku = 284758393
Color = "gray"`, Color = "gray"`,
gen: func() test { gen: func() test {
type Product struct { type Product struct {
@@ -481,6 +514,7 @@ B = "data"`,
type Doc struct { type Doc struct {
Products []Product Products []Product
} }
return test{ return test{
target: &Doc{}, target: &Doc{},
expected: &Doc{ expected: &Doc{
@@ -498,13 +532,13 @@ B = "data"`,
input: `[[Products]] input: `[[Products]]
Name = "Hammer" Name = "Hammer"
Sku = 738594937 Sku = 738594937
[[Products]] # empty table within the array [[Products]] # empty table within the array
[[Products]] [[Products]]
Name = "Nail" Name = "Nail"
Sku = 284758393 Sku = 284758393
Color = "gray"`, Color = "gray"`,
gen: func() test { gen: func() test {
return test{ return test{
@@ -654,6 +688,7 @@ B = "data"`,
A *[]*string A *[]*string
} }
hello := "Hello" hello := "Hello"
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{ expected: &doc{
@@ -673,6 +708,7 @@ B = "data"`,
type doc struct { type doc struct {
A interface{} A interface{}
} }
return test{ return test{
target: &doc{ target: &doc{
A: inner{ A: inner{
@@ -700,6 +736,7 @@ B = "data"`,
type doc struct { type doc struct {
A [4]inner A [4]inner
} }
return test{ return test{
target: &doc{}, target: &doc{},
expected: &doc{ expected: &doc{
@@ -716,6 +753,7 @@ B = "data"`,
input: "A = 1\r\n\r\nB = 2", input: "A = 1\r\n\r\nB = 2",
gen: func() test { gen: func() test {
doc := map[string]interface{}{} doc := map[string]interface{}{}
return test{ return test{
target: &doc, target: &doc,
expected: &map[string]interface{}{ expected: &map[string]interface{}{
@@ -730,6 +768,7 @@ B = "data"`,
input: "A = 1\r", input: "A = 1\r",
gen: func() test { gen: func() test {
doc := map[string]interface{}{} doc := map[string]interface{}{}
return test{ return test{
target: &doc, target: &doc,
err: true, err: true,
@@ -741,6 +780,7 @@ B = "data"`,
input: "A = 1\rB = 2", input: "A = 1\rB = 2",
gen: func() test { gen: func() test {
doc := map[string]interface{}{} doc := map[string]interface{}{}
return test{ return test{
target: &doc, target: &doc,
err: true, err: true,
@@ -752,6 +792,7 @@ B = "data"`,
input: `a = 1z = 2`, input: `a = 1z = 2`,
gen: func() test { gen: func() test {
m := map[string]interface{}{} m := map[string]interface{}{}
return test{ return test{
target: &m, target: &m,
err: true, err: true,
@@ -761,7 +802,10 @@ B = "data"`,
} }
for _, e := range examples { for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) { t.Run(e.desc, func(t *testing.T) {
t.Parallel()
if e.skip { if e.skip {
t.Skip() t.Skip()
} }
@@ -791,7 +835,7 @@ func (i Integer484) MarshalText() ([]byte, error) {
func (i *Integer484) UnmarshalText(data []byte) error { func (i *Integer484) UnmarshalText(data []byte) error {
conv, err := strconv.Atoi(string(data)) conv, err := strconv.Atoi(string(data))
if err != nil { if err != nil {
return err return fmt.Errorf("UnmarshalText: %w", err)
} }
i.Value = conv i.Value = conv
return nil return nil
@@ -803,6 +847,7 @@ type Config484 struct {
func TestIssue484(t *testing.T) { func TestIssue484(t *testing.T) {
raw := []byte(`integers = ["1","2","3","100"]`) raw := []byte(`integers = ["1","2","3","100"]`)
var cfg Config484 var cfg Config484
err := toml.Unmarshal(raw, &cfg) err := toml.Unmarshal(raw, &cfg)
require.NoError(t, err) require.NoError(t, err)
@@ -864,6 +909,7 @@ func TestIssue494(t *testing.T) {
foo = 2021-04-08 foo = 2021-04-08
bar = 2021-04-08 bar = 2021-04-08
` `
type s struct { type s struct {
Foo time.Time `toml:"foo"` Foo time.Time `toml:"foo"`
Bar time.Time `toml:"bar"` Bar time.Time `toml:"bar"`
@@ -880,7 +926,10 @@ func TestIssue507(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
//nolint:funlen
func TestUnmarshalDecodeErrors(t *testing.T) { func TestUnmarshalDecodeErrors(t *testing.T) {
t.Parallel()
examples := []struct { examples := []struct {
desc string desc string
data string data string
@@ -955,14 +1004,19 @@ world'`,
} }
for _, e := range examples { for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) { t.Run(e.desc, func(t *testing.T) {
t.Parallel()
m := map[string]interface{}{} m := map[string]interface{}{}
err := toml.Unmarshal([]byte(e.data), &m) err := toml.Unmarshal([]byte(e.data), &m)
require.Error(t, err) require.Error(t, err)
de, ok := err.(*toml.DecodeError)
if !ok { var de *toml.DecodeError
if !errors.As(err, &de) {
t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err) t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err)
} }
if e.msg != "" { if e.msg != "" {
t.Log("\n" + de.String()) t.Log("\n" + de.String())
require.Equal(t, e.msg, de.Error()) require.Equal(t, e.msg, de.Error())
@@ -971,6 +1025,7 @@ world'`,
} }
} }
//nolint:funlen
func TestLocalDateTime(t *testing.T) { func TestLocalDateTime(t *testing.T) {
t.Parallel() t.Parallel()
@@ -1058,6 +1113,7 @@ func TestIssue508(t *testing.T) {
type head struct { type head struct {
Title string `toml:"title"` Title string `toml:"title"`
} }
type text struct { type text struct {
head head
} }
@@ -1070,7 +1126,10 @@ func TestIssue508(t *testing.T) {
require.Equal(t, "This is a title", t1.head.Title) require.Equal(t, "This is a title", t1.head.Title)
} }
//nolint:funlen
func TestDecoderStrict(t *testing.T) { func TestDecoderStrict(t *testing.T) {
t.Parallel()
examples := []struct { examples := []struct {
desc string desc string
input string input string
@@ -1139,7 +1198,10 @@ bar = 42
} }
for _, e := range examples { for _, e := range examples {
e := e
t.Run(e.desc, func(t *testing.T) { t.Run(e.desc, func(t *testing.T) {
t.Parallel()
r := strings.NewReader(e.input) r := strings.NewReader(e.input)
d := toml.NewDecoder(r) d := toml.NewDecoder(r)
d.SetStrict(true) d.SetStrict(true)
@@ -1148,8 +1210,13 @@ bar = 42
x = &struct{}{} x = &struct{}{}
} }
err := d.Decode(x) err := d.Decode(x)
details := err.(*toml.StrictMissingError)
equalStringsIgnoreNewlines(t, e.expected, details.String()) var tsm *toml.StrictMissingError
if errors.As(err, &tsm) {
equalStringsIgnoreNewlines(t, e.expected, tsm.String())
} else {
t.Fatalf("err should have been a *toml.StrictMissingError, but got %s (%T)", err, err)
}
}) })
} }
} }
@@ -1171,11 +1238,15 @@ key3 = "value3"
err := d.Decode(&s) err := d.Decode(&s)
fmt.Println(err.Error()) fmt.Println(err.Error())
// Output: strict mode: fields in the document are missing in the target struct
details := err.(*toml.StrictMissingError) var details *toml.StrictMissingError
if !errors.As(err, &details) {
panic(fmt.Sprintf("err should have been a *toml.StrictMissingError, but got %s (%T)", err, err))
}
fmt.Println(details.String()) fmt.Println(details.String())
// Ouput: // Output:
// strict mode: fields in the document are missing in the target struct
// 2| key1 = "value1" // 2| key1 = "value1"
// 3| key2 = "value2" // 3| key2 = "value2"
// | ~~~~ missing field // | ~~~~ missing field