mirror of https://github.com/docker/cli.git
Merge pull request #2611 from thaJeztah/bump_mapstructure
vendor: mitchellh/mapstructure v1.3.2
This commit is contained in:
commit
674c80af89
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
module github.com/mitchellh/mapstructure
|
module github.com/mitchellh/mapstructure
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue