Merge pull request #2611 from thaJeztah/bump_mapstructure

vendor: mitchellh/mapstructure v1.3.2
This commit is contained in:
Silvin Lubecki 2020-07-06 10:49:05 +02:00 committed by GitHub
commit 674c80af89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 450 additions and 70 deletions

View File

@ -44,7 +44,7 @@ github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644
github.com/Microsoft/go-winio 6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14 github.com/Microsoft/go-winio 6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
github.com/Microsoft/hcsshim 5bc557dd210ff2caf615e6e22d398123de77fc11 # v0.8.9 github.com/Microsoft/hcsshim 5bc557dd210ff2caf615e6e22d398123de77fc11 # v0.8.9
github.com/miekg/pkcs11 210dc1e16747c5ba98a03bcbcf728c38086ea357 # v1.0.3 github.com/miekg/pkcs11 210dc1e16747c5ba98a03bcbcf728c38086ea357 # v1.0.3
github.com/mitchellh/mapstructure fa473d140ef3c6adf42d6b391fe76707f1f243c8 # v1.0.0 github.com/mitchellh/mapstructure d16e9488127408e67948eb43b6d3fbb9f222da10 # v1.3.2
github.com/moby/buildkit ae7ff7174f73bcb4df89b97e1623b3fb0bfb0a0c github.com/moby/buildkit ae7ff7174f73bcb4df89b97e1623b3fb0bfb0a0c
github.com/moby/sys 6154f11e6840c0d6b0dbb23f4125a6134b3013c9 # mountinfo/v0.1.3 github.com/moby/sys 6154f11e6840c0d6b0dbb23f4125a6134b3013c9 # mountinfo/v0.1.3
github.com/moby/term 73f35e472e8f0a3f91347164138ce6bd73b756a9 github.com/moby/term 73f35e472e8f0a3f91347164138ce6bd73b756a9

View File

@ -2,6 +2,8 @@ package mapstructure
import ( import (
"errors" "errors"
"fmt"
"net"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -115,6 +117,50 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
} }
} }
// StringToIPHookFunc returns a DecodeHookFunc that converts
// strings to net.IP
func StringToIPHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(net.IP{}) {
return data, nil
}
// Convert it by parsing
ip := net.ParseIP(data.(string))
if ip == nil {
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
}
return ip, nil
}
}
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
// strings to net.IPNet
func StringToIPNetHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(net.IPNet{}) {
return data, nil
}
// Convert it by parsing
_, net, err := net.ParseCIDR(data.(string))
return net, err
}
}
// StringToTimeHookFunc returns a DecodeHookFunc that converts // StringToTimeHookFunc returns a DecodeHookFunc that converts
// strings to time.Time. // strings to time.Time.
func StringToTimeHookFunc(layout string) DecodeHookFunc { func StringToTimeHookFunc(layout string) DecodeHookFunc {

View File

@ -1 +1,3 @@
module github.com/mitchellh/mapstructure module github.com/mitchellh/mapstructure
go 1.14

View File

@ -1,10 +1,150 @@
// Package mapstructure exposes functionality to convert an arbitrary // Package mapstructure exposes functionality to convert one arbitrary
// map[string]interface{} into a native Go structure. // Go type into another, typically to convert a map[string]interface{}
// into a native Go structure.
// //
// The Go structure can be arbitrarily complex, containing slices, // The Go structure can be arbitrarily complex, containing slices,
// other structs, etc. and the decoder will properly decode nested // other structs, etc. and the decoder will properly decode nested
// maps and so on into the proper structures in the native Go struct. // maps and so on into the proper structures in the native Go struct.
// See the examples to see what the decoder is capable of. // See the examples to see what the decoder is capable of.
//
// The simplest function to start with is Decode.
//
// Field Tags
//
// When decoding to a struct, mapstructure will use the field name by
// default to perform the mapping. For example, if a struct has a field
// "Username" then mapstructure will look for a key in the source value
// of "username" (case insensitive).
//
// type User struct {
// Username string
// }
//
// You can change the behavior of mapstructure by using struct tags.
// The default struct tag that mapstructure looks for is "mapstructure"
// but you can customize it using DecoderConfig.
//
// Renaming Fields
//
// To rename the key that mapstructure looks for, use the "mapstructure"
// tag and set a value directly. For example, to change the "username" example
// above to "user":
//
// type User struct {
// Username string `mapstructure:"user"`
// }
//
// Embedded Structs and Squashing
//
// Embedded structs are treated as if they're another field with that name.
// By default, the two structs below are equivalent when decoding with
// mapstructure:
//
// type Person struct {
// Name string
// }
//
// type Friend struct {
// Person
// }
//
// type Friend struct {
// Person Person
// }
//
// This would require an input that looks like below:
//
// map[string]interface{}{
// "person": map[string]interface{}{"name": "alice"},
// }
//
// If your "person" value is NOT nested, then you can append ",squash" to
// your tag value and mapstructure will treat it as if the embedded struct
// were part of the struct directly. Example:
//
// type Friend struct {
// Person `mapstructure:",squash"`
// }
//
// Now the following input would be accepted:
//
// map[string]interface{}{
// "name": "alice",
// }
//
// DecoderConfig has a field that changes the behavior of mapstructure
// to always squash embedded structs.
//
// Remainder Values
//
// If there are any unmapped keys in the source value, mapstructure by
// default will silently ignore them. You can error by setting ErrorUnused
// in DecoderConfig. If you're using Metadata you can also maintain a slice
// of the unused keys.
//
// You can also use the ",remain" suffix on your tag to collect all unused
// values in a map. The field with this tag MUST be a map type and should
// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
// See example below:
//
// type Friend struct {
// Name string
// Other map[string]interface{} `mapstructure:",remain"`
// }
//
// Given the input below, Other would be populated with the other
// values that weren't used (everything but "name"):
//
// map[string]interface{}{
// "name": "bob",
// "address": "123 Maple St.",
// }
//
// Omit Empty Values
//
// When decoding from a struct to any other value, you may use the
// ",omitempty" suffix on your tag to omit that value if it equates to
// the zero value. The zero value of all types is specified in the Go
// specification.
//
// For example, the zero type of a numeric type is zero ("0"). If the struct
// field value is zero and a numeric type, the field is empty, and it won't
// be encoded into the destination type.
//
// type Source {
// Age int `mapstructure:",omitempty"`
// }
//
// Unexported fields
//
// Since unexported (private) struct fields cannot be set outside the package
// where they are defined, the decoder will simply skip them.
//
// For this output type definition:
//
// type Exported struct {
// private string // this unexported field will be skipped
// Public string
// }
//
// Using this map as input:
//
// map[string]interface{}{
// "private": "I will be ignored",
// "Public": "I made it through!",
// }
//
// The following struct will be decoded:
//
// type Exported struct {
// private: "" // field is left with an empty string (zero value)
// Public: "I made it through!"
// }
//
// Other Configuration
//
// mapstructure is highly configurable. See the DecoderConfig struct
// for other features and options that are supported.
package mapstructure package mapstructure
import ( import (
@ -80,6 +220,14 @@ type DecoderConfig struct {
// //
WeaklyTypedInput bool WeaklyTypedInput bool
// Squash will squash embedded structs. A squash tag may also be
// added to an individual struct field using a tag. For example:
//
// type Parent struct {
// Child `mapstructure:",squash"`
// }
Squash bool
// Metadata is the struct that will contain extra metadata about // Metadata is the struct that will contain extra metadata about
// the decoding. If this is nil, then no metadata will be tracked. // the decoding. If this is nil, then no metadata will be tracked.
Metadata *Metadata Metadata *Metadata
@ -224,6 +372,17 @@ func (d *Decoder) Decode(input interface{}) error {
// Decodes an unknown data type into a specific reflection value. // Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
var inputVal reflect.Value
if input != nil {
inputVal = reflect.ValueOf(input)
// We need to check here if input is a typed nil. Typed nils won't
// match the "input == nil" below so we check that here.
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() {
input = nil
}
}
if input == nil { if input == nil {
// If the data is nil, then we don't set anything, unless ZeroFields is set // If the data is nil, then we don't set anything, unless ZeroFields is set
// to true. // to true.
@ -237,7 +396,6 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
return nil return nil
} }
inputVal := reflect.ValueOf(input)
if !inputVal.IsValid() { if !inputVal.IsValid() {
// If the input value is invalid, then we just set the value // If the input value is invalid, then we just set the value
// to be the zero value. // to be the zero value.
@ -260,8 +418,9 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
} }
var err error var err error
inputKind := getKind(outVal) outputKind := getKind(outVal)
switch inputKind { addMetaKey := true
switch outputKind {
case reflect.Bool: case reflect.Bool:
err = d.decodeBool(name, input, outVal) err = d.decodeBool(name, input, outVal)
case reflect.Interface: case reflect.Interface:
@ -279,7 +438,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
case reflect.Map: case reflect.Map:
err = d.decodeMap(name, input, outVal) err = d.decodeMap(name, input, outVal)
case reflect.Ptr: case reflect.Ptr:
err = d.decodePtr(name, input, outVal) addMetaKey, err = d.decodePtr(name, input, outVal)
case reflect.Slice: case reflect.Slice:
err = d.decodeSlice(name, input, outVal) err = d.decodeSlice(name, input, outVal)
case reflect.Array: case reflect.Array:
@ -288,12 +447,12 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
err = d.decodeFunc(name, input, outVal) err = d.decodeFunc(name, input, outVal)
default: default:
// If we reached this point then we weren't able to decode it // If we reached this point then we weren't able to decode it
return fmt.Errorf("%s: unsupported type: %s", name, inputKind) return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
} }
// If we reached here, then we successfully decoded SOMETHING, so // If we reached here, then we successfully decoded SOMETHING, so
// mark the key as used if we're tracking metainput. // mark the key as used if we're tracking metainput.
if d.config.Metadata != nil && name != "" { if addMetaKey && d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
} }
@ -304,9 +463,45 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// value to "data" of that type. // value to "data" of that type.
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
if val.IsValid() && val.Elem().IsValid() { if val.IsValid() && val.Elem().IsValid() {
return d.decode(name, data, val.Elem()) elem := val.Elem()
// If we can't address this element, then its not writable. Instead,
// we make a copy of the value (which is a pointer and therefore
// writable), decode into that, and replace the whole value.
copied := false
if !elem.CanAddr() {
copied = true
// Make *T
copy := reflect.New(elem.Type())
// *T = elem
copy.Elem().Set(elem)
// Set elem so we decode into it
elem = copy
} }
// Decode. If we have an error then return. We also return right
// away if we're not a copy because that means we decoded directly.
if err := d.decode(name, data, elem); err != nil || !copied {
return err
}
// If we're a copy, we need to set te final result
val.Set(elem.Elem())
return nil
}
dataVal := reflect.ValueOf(data) dataVal := reflect.ValueOf(data)
// If the input data is a pointer, and the assigned type is the dereference
// of that exact pointer, then indirect it so that we can assign it.
// Example: *string to string
if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() {
dataVal = reflect.Indirect(dataVal)
}
if !dataVal.IsValid() { if !dataVal.IsValid() {
dataVal = reflect.Zero(val.Type()) dataVal = reflect.Zero(val.Type())
} }
@ -323,7 +518,7 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value)
} }
func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.ValueOf(data) dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal) dataKind := getKind(dataVal)
converted := true converted := true
@ -375,7 +570,7 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value)
} }
func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.ValueOf(data) dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal) dataKind := getKind(dataVal)
dataType := dataVal.Type() dataType := dataVal.Type()
@ -417,8 +612,9 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
} }
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.ValueOf(data) dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal) dataKind := getKind(dataVal)
dataType := dataVal.Type()
switch { switch {
case dataKind == reflect.Int: case dataKind == reflect.Int:
@ -450,6 +646,18 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
} else { } else {
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
} }
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
jn := data.(json.Number)
i, err := jn.Int64()
if err != nil {
return fmt.Errorf(
"error decoding json.Number into %s: %s", name, err)
}
if i < 0 && !d.config.WeaklyTypedInput {
return fmt.Errorf("cannot parse '%s', %d overflows uint",
name, i)
}
val.SetUint(uint64(i))
default: default:
return fmt.Errorf( return fmt.Errorf(
"'%s' expected type '%s', got unconvertible type '%s'", "'%s' expected type '%s', got unconvertible type '%s'",
@ -460,7 +668,7 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
} }
func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.ValueOf(data) dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal) dataKind := getKind(dataVal)
switch { switch {
@ -491,7 +699,7 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
} }
func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.ValueOf(data) dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal) dataKind := getKind(dataVal)
dataType := dataVal.Type() dataType := dataVal.Type()
@ -595,6 +803,20 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle
// Accumulate errors // Accumulate errors
errors := make([]string, 0) errors := make([]string, 0)
// If the input data is empty, then we just match what the input data is.
if dataVal.Len() == 0 {
if dataVal.IsNil() {
if !val.IsNil() {
val.Set(dataVal)
}
} else {
// Set to empty allocated value
val.Set(valMap)
}
return nil
}
for _, k := range dataVal.MapKeys() { for _, k := range dataVal.MapKeys() {
fieldName := fmt.Sprintf("%s[%s]", name, k) fieldName := fmt.Sprintf("%s[%s]", name, k)
@ -645,28 +867,32 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
} }
tagValue := f.Tag.Get(d.config.TagName) tagValue := f.Tag.Get(d.config.TagName)
tagParts := strings.Split(tagValue, ",")
// Determine the name of the key in the map
keyName := f.Name keyName := f.Name
if tagParts[0] != "" {
if tagParts[0] == "-" { // If Squash is set in the config, we squash the field down.
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
// Determine the name of the key in the map
if index := strings.Index(tagValue, ","); index != -1 {
if tagValue[:index] == "-" {
continue continue
} }
keyName = tagParts[0] // If "omitempty" is specified in the tag, it ignores empty values.
if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
continue
} }
// If "squash" is specified in the tag, we squash the field down. // If "squash" is specified in the tag, we squash the field down.
squash := false squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1
for _, tag := range tagParts[1:] {
if tag == "squash" {
squash = true
break
}
}
if squash && v.Kind() != reflect.Struct { if squash && v.Kind() != reflect.Struct {
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
} }
keyName = tagValue[:index]
} else if len(tagValue) > 0 {
if tagValue == "-" {
continue
}
keyName = tagValue
}
switch v.Kind() { switch v.Kind() {
// this is an embedded struct, so handle it differently // this is an embedded struct, so handle it differently
@ -705,12 +931,34 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
return nil return nil
} }
func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) {
// If the input data is nil, then we want to just set the output
// pointer to be nil as well.
isNil := data == nil
if !isNil {
switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {
case reflect.Chan,
reflect.Func,
reflect.Interface,
reflect.Map,
reflect.Ptr,
reflect.Slice:
isNil = v.IsNil()
}
}
if isNil {
if !val.IsNil() && val.CanSet() {
nilValue := reflect.New(val.Type()).Elem()
val.Set(nilValue)
}
return true, nil
}
// Create an element of the concrete (non pointer) type and decode // Create an element of the concrete (non pointer) type and decode
// into that. Then set the value of the pointer to this type. // into that. Then set the value of the pointer to this type.
valType := val.Type() valType := val.Type()
valElemType := valType.Elem() valElemType := valType.Elem()
if val.CanSet() { if val.CanSet() {
realVal := val realVal := val
if realVal.IsNil() || d.config.ZeroFields { if realVal.IsNil() || d.config.ZeroFields {
@ -718,16 +966,16 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er
} }
if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
return err return false, err
} }
val.Set(realVal) val.Set(realVal)
} else { } else {
if err := d.decode(name, data, reflect.Indirect(val)); err != nil { if err := d.decode(name, data, reflect.Indirect(val)); err != nil {
return err return false, err
} }
} }
return nil return false, nil
} }
func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error {
@ -750,12 +998,14 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
valElemType := valType.Elem() valElemType := valType.Elem()
sliceType := reflect.SliceOf(valElemType) sliceType := reflect.SliceOf(valElemType)
valSlice := val // If we have a non array/slice type then we first attempt to convert.
if valSlice.IsNil() || d.config.ZeroFields {
// Check input type
if dataValKind != reflect.Array && dataValKind != reflect.Slice { if dataValKind != reflect.Array && dataValKind != reflect.Slice {
if d.config.WeaklyTypedInput { if d.config.WeaklyTypedInput {
switch { switch {
// Slice and array we use the normal logic
case dataValKind == reflect.Slice, dataValKind == reflect.Array:
break
// Empty maps turn into empty slices // Empty maps turn into empty slices
case dataValKind == reflect.Map: case dataValKind == reflect.Map:
if dataVal.Len() == 0 { if dataVal.Len() == 0 {
@ -767,6 +1017,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
return d.decodeSlice(name, []byte(dataVal.String()), val) return d.decodeSlice(name, []byte(dataVal.String()), val)
// All other types we try to convert to the slice type // All other types we try to convert to the slice type
// and "lift" it into it. i.e. a string becomes a string slice. // and "lift" it into it. i.e. a string becomes a string slice.
default: default:
@ -774,11 +1025,18 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
return d.decodeSlice(name, []interface{}{data}, val) return d.decodeSlice(name, []interface{}{data}, val)
} }
} }
return fmt.Errorf( return fmt.Errorf(
"'%s': source data must be an array or slice, got %s", name, dataValKind) "'%s': source data must be an array or slice, got %s", name, dataValKind)
} }
// If the input value is nil, then don't allocate since empty != nil
if dataVal.IsNil() {
return nil
}
valSlice := val
if valSlice.IsNil() || d.config.ZeroFields {
// Make a new slice to hold our result, same size as the original data. // Make a new slice to hold our result, same size as the original data.
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
} }
@ -888,10 +1146,29 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
} }
dataValKind := dataVal.Kind() dataValKind := dataVal.Kind()
if dataValKind != reflect.Map { switch dataValKind {
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataValKind) case reflect.Map:
return d.decodeStructFromMap(name, dataVal, val)
case reflect.Struct:
// Not the most efficient way to do this but we can optimize later if
// we want to. To convert from struct to struct we go to map first
// as an intermediary.
m := make(map[string]interface{})
mval := reflect.Indirect(reflect.ValueOf(&m))
if err := d.decodeMapFromStruct(name, dataVal, mval, mval); err != nil {
return err
} }
result := d.decodeStructFromMap(name, mval, val)
return result
default:
return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
}
}
func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {
dataValType := dataVal.Type() dataValType := dataVal.Type()
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
return fmt.Errorf( return fmt.Errorf(
@ -920,6 +1197,11 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
field reflect.StructField field reflect.StructField
val reflect.Value val reflect.Value
} }
// remainField is set to a valid field set with the "remain" tag if
// we are keeping track of remaining values.
var remainField *field
fields := []field{} fields := []field{}
for len(structs) > 0 { for len(structs) > 0 {
structVal := structs[0] structVal := structs[0]
@ -932,13 +1214,21 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
fieldKind := fieldType.Type.Kind() fieldKind := fieldType.Type.Kind()
// If "squash" is specified in the tag, we squash the field down. // If "squash" is specified in the tag, we squash the field down.
squash := false squash := d.config.Squash && fieldKind == reflect.Struct && fieldType.Anonymous
remain := false
// We always parse the tags cause we're looking for other tags too
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
for _, tag := range tagParts[1:] { for _, tag := range tagParts[1:] {
if tag == "squash" { if tag == "squash" {
squash = true squash = true
break break
} }
if tag == "remain" {
remain = true
break
}
} }
if squash { if squash {
@ -951,10 +1241,15 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
continue continue
} }
// Build our field
if remain {
remainField = &field{fieldType, structVal.Field(i)}
} else {
// Normal struct field, store it away // Normal struct field, store it away
fields = append(fields, field{fieldType, structVal.Field(i)}) fields = append(fields, field{fieldType, structVal.Field(i)})
} }
} }
}
// for fieldType, field := range fields { // for fieldType, field := range fields {
for _, f := range fields { for _, f := range fields {
@ -993,9 +1288,6 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
} }
} }
// Delete the key we're using from the unused map so we stop tracking
delete(dataValKeysUnused, rawMapKey.Interface())
if !fieldValue.IsValid() { if !fieldValue.IsValid() {
// This should never happen // This should never happen
panic("field is not valid") panic("field is not valid")
@ -1007,6 +1299,9 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
continue continue
} }
// Delete the key we're using from the unused map so we stop tracking
delete(dataValKeysUnused, rawMapKey.Interface())
// If the name is empty string, then we're at the root, and we // If the name is empty string, then we're at the root, and we
// don't dot-join the fields. // don't dot-join the fields.
if name != "" { if name != "" {
@ -1018,6 +1313,25 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
} }
} }
// If we have a "remain"-tagged field and we have unused keys then
// we put the unused keys directly into the remain field.
if remainField != nil && len(dataValKeysUnused) > 0 {
// Build a map of only the unused values
remain := map[interface{}]interface{}{}
for key := range dataValKeysUnused {
remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()
}
// Decode it as-if we were just decoding this map onto our map.
if err := d.decodeMap(name, remain, remainField.val); err != nil {
errors = appendErrors(errors, err)
}
// Set the map to nil so we have none so that the next check will
// not error (ErrorUnused)
dataValKeysUnused = nil
}
if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {
keys := make([]string, 0, len(dataValKeysUnused)) keys := make([]string, 0, len(dataValKeysUnused))
for rawKey := range dataValKeysUnused { for rawKey := range dataValKeysUnused {
@ -1048,6 +1362,24 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
return nil return nil
} }
func isEmptyValue(v reflect.Value) bool {
switch getKind(v) {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
}
return false
}
func getKind(val reflect.Value) reflect.Kind { func getKind(val reflect.Value) reflect.Kind {
kind := val.Kind() kind := val.Kind()