summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mitchellh/mapstructure/mapstructure.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mitchellh/mapstructure/mapstructure.go')
-rw-r--r--vendor/github.com/mitchellh/mapstructure/mapstructure.go333
1 files changed, 290 insertions, 43 deletions
diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
index 256ee63fbf..b384d9d928 100644
--- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
@@ -1,10 +1,150 @@
-// Package mapstructure exposes functionality to convert an arbitrary
-// map[string]interface{} into a native Go structure.
+// Package mapstructure exposes functionality to convert one arbitrary
+// 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,
// other structs, etc. and the decoder will properly decode nested
// maps and so on into the proper structures in the native Go struct.
// 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
import (
@@ -80,6 +220,14 @@ type DecoderConfig struct {
//
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
// the decoding. If this is nil, then no metadata will be tracked.
Metadata *Metadata
@@ -271,6 +419,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
var err error
outputKind := getKind(outVal)
+ addMetaKey := true
switch outputKind {
case reflect.Bool:
err = d.decodeBool(name, input, outVal)
@@ -289,7 +438,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
case reflect.Map:
err = d.decodeMap(name, input, outVal)
case reflect.Ptr:
- err = d.decodePtr(name, input, outVal)
+ addMetaKey, err = d.decodePtr(name, input, outVal)
case reflect.Slice:
err = d.decodeSlice(name, input, outVal)
case reflect.Array:
@@ -303,7 +452,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// If we reached here, then we successfully decoded SOMETHING, so
// 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)
}
@@ -314,7 +463,34 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// value to "data" of that type.
func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
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)
@@ -438,6 +614,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
+ dataType := dataVal.Type()
switch {
case dataKind == reflect.Int:
@@ -469,6 +646,18 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
} else {
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:
return fmt.Errorf(
"'%s' expected type '%s', got unconvertible type '%s'",
@@ -678,27 +867,31 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
}
tagValue := f.Tag.Get(d.config.TagName)
- tagParts := strings.Split(tagValue, ",")
+ keyName := f.Name
+ // 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
- keyName := f.Name
- if tagParts[0] != "" {
- if tagParts[0] == "-" {
+ if index := strings.Index(tagValue, ","); index != -1 {
+ if tagValue[:index] == "-" {
+ continue
+ }
+ // If "omitempty" is specified in the tag, it ignores empty values.
+ if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
continue
}
- keyName = tagParts[0]
- }
- // If "squash" is specified in the tag, we squash the field down.
- squash := false
- for _, tag := range tagParts[1:] {
- if tag == "squash" {
- squash = true
- break
+ // If "squash" is specified in the tag, we squash the field down.
+ squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1
+ if squash && v.Kind() != reflect.Struct {
+ return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
}
- }
- if squash && v.Kind() != reflect.Struct {
- 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() {
@@ -738,7 +931,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
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
@@ -759,7 +952,7 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er
val.Set(nilValue)
}
- return nil
+ return true, nil
}
// Create an element of the concrete (non pointer) type and decode
@@ -773,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 {
- return err
+ return false, err
}
val.Set(realVal)
} else {
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 {
@@ -805,8 +998,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
valElemType := valType.Elem()
sliceType := reflect.SliceOf(valElemType)
- valSlice := val
- if valSlice.IsNil() || d.config.ZeroFields {
+ // If we have a non array/slice type then we first attempt to convert.
+ if dataValKind != reflect.Array && dataValKind != reflect.Slice {
if d.config.WeaklyTypedInput {
switch {
// Slice and array we use the normal logic
@@ -833,18 +1026,17 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
}
}
- // Check input type
- if dataValKind != reflect.Array && dataValKind != reflect.Slice {
- return fmt.Errorf(
- "'%s': source data must be an array or slice, got %s", name, dataValKind)
-
- }
+ return fmt.Errorf(
+ "'%s': source data must be an array or slice, got %s", name, dataValKind)
+ }
- // If the input value is empty, then don't allocate since non-nil != nil
- if dataVal.Len() == 0 {
- return nil
- }
+ // 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.
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
}
@@ -1005,6 +1197,11 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
field reflect.StructField
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{}
for len(structs) > 0 {
structVal := structs[0]
@@ -1017,13 +1214,21 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
fieldKind := fieldType.Type.Kind()
// 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), ",")
for _, tag := range tagParts[1:] {
if tag == "squash" {
squash = true
break
}
+
+ if tag == "remain" {
+ remain = true
+ break
+ }
}
if squash {
@@ -1036,8 +1241,13 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
continue
}
- // Normal struct field, store it away
- fields = append(fields, field{fieldType, structVal.Field(i)})
+ // Build our field
+ if remain {
+ remainField = &field{fieldType, structVal.Field(i)}
+ } else {
+ // Normal struct field, store it away
+ fields = append(fields, field{fieldType, structVal.Field(i)})
+ }
}
}
@@ -1078,9 +1288,6 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
}
- // Delete the key we're using from the unused map so we stop tracking
- delete(dataValKeysUnused, rawMapKey.Interface())
-
if !fieldValue.IsValid() {
// This should never happen
panic("field is not valid")
@@ -1092,6 +1299,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
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
// don't dot-join the fields.
if name != "" {
@@ -1103,6 +1313,25 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
}
+ // 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 {
keys := make([]string, 0, len(dataValKeysUnused))
for rawKey := range dataValKeysUnused {
@@ -1133,6 +1362,24 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
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 {
kind := val.Kind()