--- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go Tue Aug 23 22:33:28 2022 +0200
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go Tue Aug 23 22:39:43 2022 +0200
@@ -122,7 +122,7 @@
// field value is zero and a numeric type, the field is empty, and it won't
// be encoded into the destination type.
//
-// type Source {
+// type Source struct {
// Age int `mapstructure:",omitempty"`
// }
//
@@ -192,7 +192,7 @@
// 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
+// DecodeHookFuncValue 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)
@@ -215,6 +215,12 @@
// (extra keys).
ErrorUnused bool
+ // If ErrorUnset is true, then it is an error for there to exist
+ // fields in the result that were not set in the decoding process
+ // (extra fields). This only applies to decoding to a struct. This
+ // will affect all nested structs as well.
+ ErrorUnset bool
+
// ZeroFields, if set to true, will zero fields before writing them.
// For example, a map will be emptied before decoded values are put in
// it. If this is false, a map will be merged.
@@ -258,6 +264,15 @@
// The tag name that mapstructure reads for field names. This
// defaults to "mapstructure"
TagName string
+
+ // IgnoreUntaggedFields ignores all struct fields without explicit
+ // TagName, comparable to `mapstructure:"-"` as default behaviour.
+ IgnoreUntaggedFields bool
+
+ // MatchName is the function used to match the map key to the struct
+ // field name or tag. Defaults to `strings.EqualFold`. This can be used
+ // to implement case-sensitive tag values, support snake casing, etc.
+ MatchName func(mapKey, fieldName string) bool
}
// A Decoder takes a raw interface value and turns it into structured
@@ -279,6 +294,11 @@
// Unused is a slice of keys that were found in the raw value but
// weren't decoded since there was no matching field in the result interface
Unused []string
+
+ // Unset is a slice of field names that were found in the result interface
+ // but weren't set in the decoding process since there was no matching value
+ // in the input
+ Unset []string
}
// Decode takes an input structure and uses reflection to translate it to
@@ -370,12 +390,20 @@
if config.Metadata.Unused == nil {
config.Metadata.Unused = make([]string, 0)
}
+
+ if config.Metadata.Unset == nil {
+ config.Metadata.Unset = make([]string, 0)
+ }
}
if config.TagName == "" {
config.TagName = "mapstructure"
}
+ if config.MatchName == nil {
+ config.MatchName = strings.EqualFold
+ }
+
result := &Decoder{
config: config,
}
@@ -675,16 +703,12 @@
}
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
jn := data.(json.Number)
- i, err := jn.Int64()
+ i, err := strconv.ParseUint(string(jn), 0, 64)
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))
+ val.SetUint(i)
default:
return fmt.Errorf(
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
@@ -901,9 +925,15 @@
tagValue := f.Tag.Get(d.config.TagName)
keyName := f.Name
+ if tagValue == "" && d.config.IgnoreUntaggedFields {
+ continue
+ }
+
// If Squash is set in the config, we squash the field down.
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
+ v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
+
// Determine the name of the key in the map
if index := strings.Index(tagValue, ","); index != -1 {
if tagValue[:index] == "-" {
@@ -915,7 +945,7 @@
}
// If "squash" is specified in the tag, we squash the field down.
- squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1
+ 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 {
@@ -927,7 +957,9 @@
return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
}
}
- keyName = tagValue[:index]
+ if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
+ keyName = keyNameTagValue
+ }
} else if len(tagValue) > 0 {
if tagValue == "-" {
continue
@@ -1083,7 +1115,7 @@
}
// If the input value is nil, then don't allocate since empty != nil
- if dataVal.IsNil() {
+ if dataValKind != reflect.Array && dataVal.IsNil() {
return nil
}
@@ -1245,6 +1277,7 @@
dataValKeysUnused[dataValKey.Interface()] = struct{}{}
}
+ targetValKeysUnused := make(map[interface{}]struct{})
errors := make([]string, 0)
// This slice will keep track of all the structs we'll be decoding.
@@ -1340,7 +1373,7 @@
continue
}
- if strings.EqualFold(mK, fieldName) {
+ if d.config.MatchName(mK, fieldName) {
rawMapKey = dataValKey
rawMapVal = dataVal.MapIndex(dataValKey)
break
@@ -1349,7 +1382,8 @@
if !rawMapVal.IsValid() {
// There was no matching key in the map for the value in
- // the struct. Just ignore.
+ // the struct. Remember it for potential errors and metadata.
+ targetValKeysUnused[fieldName] = struct{}{}
continue
}
}
@@ -1409,6 +1443,17 @@
errors = appendErrors(errors, err)
}
+ if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
+ keys := make([]string, 0, len(targetValKeysUnused))
+ for rawKey := range targetValKeysUnused {
+ keys = append(keys, rawKey.(string))
+ }
+ sort.Strings(keys)
+
+ err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
+ errors = appendErrors(errors, err)
+ }
+
if len(errors) > 0 {
return &Error{errors}
}
@@ -1423,6 +1468,14 @@
d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
}
+ for rawKey := range targetValKeysUnused {
+ key := rawKey.(string)
+ if name != "" {
+ key = name + "." + key
+ }
+
+ d.config.Metadata.Unset = append(d.config.Metadata.Unset, key)
+ }
}
return nil
@@ -1460,3 +1513,28 @@
return kind
}
}
+
+func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool {
+ for i := 0; i < typ.NumField(); i++ {
+ f := typ.Field(i)
+ if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
+ return true
+ }
+ if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
+ return true
+ }
+ }
+ return false
+}
+
+func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
+ if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
+ return v
+ }
+ deref := v.Elem()
+ derefT := deref.Type()
+ if isStructTypeConvertibleToMap(derefT, true, tagName) {
+ return deref
+ }
+ return v
+}