vendor/github.com/mitchellh/mapstructure/mapstructure.go
changeset 256 6d9efbef00a9
parent 251 1c52a0eeb952
child 260 445e01aede7e
--- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go	Mon Jun 07 20:58:18 2021 +0200
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go	Sun Jul 11 10:35:56 2021 +0200
@@ -1,10 +1,161 @@
-// 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",
+//     }
+//
+// When decoding from a struct to a map, the squash tag squashes the struct
+// fields into a single map. Using the example structs from above:
+//
+//     Friend{Person: Person{Name: "alice"}}
+//
+// Will be decoded into a map:
+//
+//     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 (
@@ -21,10 +172,11 @@
 // data transformations. See "DecodeHook" in the DecoderConfig
 // struct.
 //
-// The type should be DecodeHookFuncType or DecodeHookFuncKind.
-// Either is accepted. Types are a superset of Kinds (Types can return
-// Kinds) and are generally a richer thing to use, but Kinds are simpler
-// if you only need those.
+// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or
+// DecodeHookFuncValue.
+// Values are a superset of Types (Values can return types), and Types are a
+// superset of Kinds (Types can return Kinds) and are generally a richer thing
+// to use, but Kinds are simpler if you only need those.
 //
 // The reason DecodeHookFunc is multi-typed is for backwards compatibility:
 // we started with Kinds and then realized Types were the better solution,
@@ -40,15 +192,22 @@
 // source and target types.
 type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
 
+// DecodeHookFuncRaw is a DecodeHookFunc which has complete access to both the source and target
+// values.
+type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
+
 // DecoderConfig is the configuration that is used to create a new decoder
 // and allows customization of various aspects of decoding.
 type DecoderConfig struct {
 	// DecodeHook, if set, will be called before any decoding and any
 	// type conversion (if WeaklyTypedInput is on). This lets you modify
-	// the values before they're set down onto the resulting struct.
+	// the values before they're set down onto the resulting struct. The
+	// DecodeHook is called for every map and value in the input. This means
+	// that if a struct has embedded fields with squash tags the decode hook
+	// is called only once with all of the input data, not once for each
+	// embedded struct.
 	//
-	// If an error is returned, the entire decode will fail with that
-	// error.
+	// If an error is returned, the entire decode will fail with that error.
 	DecodeHook DecodeHookFunc
 
 	// If ErrorUnused is true, then it is an error for there to exist
@@ -80,6 +239,14 @@
 	//
 	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
@@ -261,9 +428,7 @@
 	if d.config.DecodeHook != nil {
 		// We have a DecodeHook, so let's pre-process the input.
 		var err error
-		input, err = DecodeHookExec(
-			d.config.DecodeHook,
-			inputVal.Type(), outVal.Type(), input)
+		input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal)
 		if err != nil {
 			return fmt.Errorf("error decoding '%s': %s", name, err)
 		}
@@ -271,6 +436,7 @@
 
 	var err error
 	outputKind := getKind(outVal)
+	addMetaKey := true
 	switch outputKind {
 	case reflect.Bool:
 		err = d.decodeBool(name, input, outVal)
@@ -289,7 +455,7 @@
 	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 +469,7 @@
 
 	// 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 +480,34 @@
 // 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)
@@ -386,8 +579,8 @@
 
 	if !converted {
 		return fmt.Errorf(
-			"'%s' expected type '%s', got unconvertible type '%s'",
-			name, val.Type(), dataVal.Type())
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
 	}
 
 	return nil
@@ -412,7 +605,12 @@
 			val.SetInt(0)
 		}
 	case dataKind == reflect.String && d.config.WeaklyTypedInput:
-		i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())
+		str := dataVal.String()
+		if str == "" {
+			str = "0"
+		}
+
+		i, err := strconv.ParseInt(str, 0, val.Type().Bits())
 		if err == nil {
 			val.SetInt(i)
 		} else {
@@ -428,8 +626,8 @@
 		val.SetInt(i)
 	default:
 		return fmt.Errorf(
-			"'%s' expected type '%s', got unconvertible type '%s'",
-			name, val.Type(), dataVal.Type())
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
 	}
 
 	return nil
@@ -438,6 +636,7 @@
 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:
@@ -463,16 +662,33 @@
 			val.SetUint(0)
 		}
 	case dataKind == reflect.String && d.config.WeaklyTypedInput:
-		i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())
+		str := dataVal.String()
+		if str == "" {
+			str = "0"
+		}
+
+		i, err := strconv.ParseUint(str, 0, val.Type().Bits())
 		if err == nil {
 			val.SetUint(i)
 		} 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'",
-			name, val.Type(), dataVal.Type())
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
 	}
 
 	return nil
@@ -502,8 +718,8 @@
 		}
 	default:
 		return fmt.Errorf(
-			"'%s' expected type '%s', got unconvertible type '%s'",
-			name, val.Type(), dataVal.Type())
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
 	}
 
 	return nil
@@ -528,7 +744,12 @@
 			val.SetFloat(0)
 		}
 	case dataKind == reflect.String && d.config.WeaklyTypedInput:
-		f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits())
+		str := dataVal.String()
+		if str == "" {
+			str = "0"
+		}
+
+		f, err := strconv.ParseFloat(str, val.Type().Bits())
 		if err == nil {
 			val.SetFloat(f)
 		} else {
@@ -544,8 +765,8 @@
 		val.SetFloat(i)
 	default:
 		return fmt.Errorf(
-			"'%s' expected type '%s', got unconvertible type '%s'",
-			name, val.Type(), dataVal.Type())
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
 	}
 
 	return nil
@@ -596,7 +817,7 @@
 
 	for i := 0; i < dataVal.Len(); i++ {
 		err := d.decode(
-			fmt.Sprintf("%s[%d]", name, i),
+			name+"["+strconv.Itoa(i)+"]",
 			dataVal.Index(i).Interface(), val)
 		if err != nil {
 			return err
@@ -629,7 +850,7 @@
 	}
 
 	for _, k := range dataVal.MapKeys() {
-		fieldName := fmt.Sprintf("%s[%s]", name, k)
+		fieldName := name + "[" + k.String() + "]"
 
 		// First decode the key into the proper type
 		currentKey := reflect.Indirect(reflect.New(valKeyType))
@@ -678,27 +899,40 @@
 		}
 
 		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 = !squash && strings.Index(tagValue[index+1:], "squash") != -1
+			if squash {
+				// When squashing, the embedded type can be a pointer to a struct.
+				if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
+					v = v.Elem()
+				}
 
-		// 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
+				// The final type must be a struct
+				if 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() {
@@ -713,11 +947,22 @@
 			mType := reflect.MapOf(vKeyType, vElemType)
 			vMap := reflect.MakeMap(mType)
 
-			err := d.decode(keyName, x.Interface(), vMap)
+			// Creating a pointer to a map so that other methods can completely
+			// overwrite the map if need be (looking at you decodeMapFromMap). The
+			// indirection allows the underlying map to be settable (CanSet() == true)
+			// where as reflect.MakeMap returns an unsettable map.
+			addrVal := reflect.New(vMap.Type())
+			reflect.Indirect(addrVal).Set(vMap)
+
+			err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal))
 			if err != nil {
 				return err
 			}
 
+			// the underlying map may have been completely overwritten so pull
+			// it indirectly out of the enclosing value.
+			vMap = reflect.Indirect(addrVal)
+
 			if squash {
 				for _, k := range vMap.MapKeys() {
 					valMap.SetMapIndex(k, vMap.MapIndex(k))
@@ -738,7 +983,7 @@
 	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 +1004,7 @@
 			val.Set(nilValue)
 		}
 
-		return nil
+		return true, nil
 	}
 
 	// Create an element of the concrete (non pointer) type and decode
@@ -773,16 +1018,16 @@
 		}
 
 		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 {
@@ -791,8 +1036,8 @@
 	dataVal := reflect.Indirect(reflect.ValueOf(data))
 	if val.Type() != dataVal.Type() {
 		return fmt.Errorf(
-			"'%s' expected type '%s', got unconvertible type '%s'",
-			name, val.Type(), dataVal.Type())
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
 	}
 	val.Set(dataVal)
 	return nil
@@ -805,8 +1050,8 @@
 	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 +1078,17 @@
 			}
 		}
 
-		// 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 nil, then don't allocate since empty != nil
+	if dataVal.IsNil() {
+		return nil
+	}
 
-		// If the input value is empty, then don't allocate since non-nil != nil
-		if dataVal.Len() == 0 {
-			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())
 	}
@@ -859,7 +1103,7 @@
 		}
 		currentField := valSlice.Index(i)
 
-		fieldName := fmt.Sprintf("%s[%d]", name, i)
+		fieldName := name + "[" + strconv.Itoa(i) + "]"
 		if err := d.decode(fieldName, currentData, currentField); err != nil {
 			errors = appendErrors(errors, err)
 		}
@@ -926,7 +1170,7 @@
 		currentData := dataVal.Index(i).Interface()
 		currentField := valArray.Index(i)
 
-		fieldName := fmt.Sprintf("%s[%d]", name, i)
+		fieldName := name + "[" + strconv.Itoa(i) + "]"
 		if err := d.decode(fieldName, currentData, currentField); err != nil {
 			errors = appendErrors(errors, err)
 		}
@@ -962,13 +1206,23 @@
 		// 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 {
+
+		// Make a new map to hold our result
+		mapType := reflect.TypeOf((map[string]interface{})(nil))
+		mval := reflect.MakeMap(mapType)
+
+		// Creating a pointer to a map so that other methods can completely
+		// overwrite the map if need be (looking at you decodeMapFromMap). The
+		// indirection allows the underlying map to be settable (CanSet() == true)
+		// where as reflect.MakeMap returns an unsettable map.
+		addrVal := reflect.New(mval.Type())
+
+		reflect.Indirect(addrVal).Set(mval)
+		if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil {
 			return err
 		}
 
-		result := d.decodeStructFromMap(name, mval, val)
+		result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val)
 		return result
 
 	default:
@@ -1005,6 +1259,11 @@
 		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]
@@ -1014,30 +1273,47 @@
 
 		for i := 0; i < structType.NumField(); i++ {
 			fieldType := structType.Field(i)
-			fieldKind := fieldType.Type.Kind()
+			fieldVal := structVal.Field(i)
+			if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {
+				// Handle embedded struct pointers as embedded structs.
+				fieldVal = fieldVal.Elem()
+			}
 
 			// If "squash" is specified in the tag, we squash the field down.
-			squash := false
+			squash := d.config.Squash && fieldVal.Kind() == 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 {
-				if fieldKind != reflect.Struct {
+				if fieldVal.Kind() != reflect.Struct {
 					errors = appendErrors(errors,
-						fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind))
+						fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
 				} else {
-					structs = append(structs, structVal.FieldByName(fieldType.Name))
+					structs = append(structs, fieldVal)
 				}
 				continue
 			}
 
-			// Normal struct field, store it away
-			fields = append(fields, field{fieldType, structVal.Field(i)})
+			// Build our field
+			if remain {
+				remainField = &field{fieldType, fieldVal}
+			} else {
+				// Normal struct field, store it away
+				fields = append(fields, field{fieldType, fieldVal})
+			}
 		}
 	}
 
@@ -1078,9 +1354,6 @@
 			}
 		}
 
-		// 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,10 +1365,13 @@
 			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 != "" {
-			fieldName = fmt.Sprintf("%s.%s", name, fieldName)
+			fieldName = name + "." + fieldName
 		}
 
 		if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {
@@ -1103,6 +1379,25 @@
 		}
 	}
 
+	// 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 {
@@ -1123,7 +1418,7 @@
 		for rawKey := range dataValKeysUnused {
 			key := rawKey.(string)
 			if name != "" {
-				key = fmt.Sprintf("%s.%s", name, key)
+				key = name + "." + key
 			}
 
 			d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
@@ -1133,6 +1428,24 @@
 	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()