diff --git a/mapstructure.go b/mapstructure.go index 9087fd96..2776df17 100644 --- a/mapstructure.go +++ b/mapstructure.go @@ -318,6 +318,12 @@ type DecoderConfig struct { // } Deep bool + // If Indirect is true, container types such as slices and maps will + // use interface{} instead of the concrete value type, most prominently in nested maps/slices/arrays; + // e.g. []interface{} instead of [][]interface{} and map[k]interface{} instead map[k]map[k]v. + // Additionally, array types will be represented as []interface{}-slices, matching json double conversion behavior. + Indirect 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 @@ -527,10 +533,129 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { return result, nil } +var interfaceType = reflect.TypeOf((*any)(nil)).Elem() + +// state propagates global/field-level configuration down the type hierarchy +type state struct { + ValueType reflect.Type + Deep bool + Indirect bool +} + +// returns new state with updated value type hint +func (s state) deepType(t reflect.Type) state { + for t.Kind() == reflect.Pointer { + t = t.Elem() + } + + switch t.Kind() { + case reflect.Struct: + s.ValueType = reflect.TypeOf(map[string]any{}) + case reflect.Slice: + s = s.deepType(t.Elem()) + + s.ValueType = reflect.SliceOf(s.ValueType) + case reflect.Array: + s = s.deepType(t.Elem()) + + if s.Indirect { + s.ValueType = reflect.SliceOf(s.ValueType) + } else { + s.ValueType = reflect.ArrayOf(t.Len(), s.ValueType) + } + case reflect.Map: + s = s.deepType(t.Elem()) + + s.ValueType = reflect.MapOf(t.Key(), s.ValueType) + default: + s.ValueType = nil + } + + return s +} + +// makeValueType instantiates hinted value type for opaque interface{} destinations +func (s state) makeValueType() reflect.Value { + switch s.ValueType.Kind() { + case reflect.Map: + var t reflect.Type + + if s.Indirect { + t = reflect.MapOf(s.ValueType.Key(), interfaceType) + } else { + t = s.ValueType + } + + addr := reflect.New(t) + + reflect.Indirect(addr).Set(reflect.MakeMap(t)) + + return indirectRoot(addr) + case reflect.Slice: + var t reflect.Type + + if s.Indirect { + t = reflect.SliceOf(interfaceType) + } else { + t = s.ValueType + } + + addr := reflect.New(t) + + reflect.Indirect(addr).Set(reflect.MakeSlice(t, 0, 0)) + + return indirectRoot(addr) + case reflect.Array: + var t reflect.Type + + if s.Indirect { + t = reflect.ArrayOf(s.ValueType.Len(), interfaceType) + } else { + t = s.ValueType + } + + addr := reflect.New(t) + + reflect.Indirect(addr).Set(reflect.Zero(t)) + + return indirectRoot(addr) + default: + return reflect.Zero(s.ValueType) + } +} + +// elem returns new state with value type hint adjusted to the nested type +func (s state) elem() state { + if s.ValueType == nil { + return s + } + + switch s.ValueType.Kind() { + case reflect.Map, reflect.Slice, reflect.Array: + s.ValueType = s.ValueType.Elem() + default: + } + + return s +} + +func indirectRoot(v reflect.Value) reflect.Value { + for v.Kind() == reflect.Pointer { + v = v.Elem() + } + + return v +} + // Decode decodes the given raw interface to the target pointer specified // by the configuration. func (d *Decoder) Decode(input any) error { - err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem()) + s := state{ + Deep: d.config.Deep, + Indirect: d.config.Indirect, + } + + err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem(), s) // Retain some of the original behavior when multiple errors ocurr var joinedErr interface{ Unwrap() []error } @@ -551,7 +676,7 @@ func isNil(input any) bool { } // Decodes an unknown data type into a specific reflection value. -func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { +func (d *Decoder) decode(name string, input any, outVal reflect.Value, s state) error { var ( inputVal = reflect.ValueOf(input) outputKind = getKind(outVal) @@ -629,7 +754,11 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { case reflect.Bool: err = d.decodeBool(name, input, outVal) case reflect.Interface: - err = d.decodeBasic(name, input, outVal) + if s.Deep && s.ValueType != nil && s.ValueType.Kind() == reflect.Map { + err = d.decodeMap(name, input, outVal, s) + } else { + err = d.decodeBasic(name, input, outVal, s) + } case reflect.String: err = d.decodeString(name, input, outVal) case reflect.Int: @@ -641,15 +770,15 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { case reflect.Complex64: err = d.decodeComplex(name, input, outVal) case reflect.Struct: - err = d.decodeStruct(name, input, outVal) + err = d.decodeStruct(name, input, outVal, s) case reflect.Map: - err = d.decodeMap(name, input, outVal) + err = d.decodeMap(name, input, outVal, s.elem()) case reflect.Ptr: - addMetaKey, err = d.decodePtr(name, input, outVal) + addMetaKey, err = d.decodePtr(name, input, outVal, s) case reflect.Slice: - err = d.decodeSlice(name, input, outVal) + err = d.decodeSlice(name, input, outVal, s.elem()) case reflect.Array: - err = d.decodeArray(name, input, outVal) + err = d.decodeArray(name, input, outVal, s.elem()) case reflect.Func: err = d.decodeFunc(name, input, outVal) default: @@ -669,7 +798,7 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error { // This decodes a basic type (bool, int, string, etc.) and sets the // value to "data" of that type. -func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error { +func (d *Decoder) decodeBasic(name string, data any, val reflect.Value, s state) error { if val.IsValid() && val.Elem().IsValid() { elem := val.Elem() @@ -692,7 +821,7 @@ func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error { // 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 { + if err := d.decode(name, data, elem, s); err != nil || !copied { return err } @@ -706,8 +835,8 @@ func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error { // 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.Kind() == reflect.Ptr && indirectRoot(dataVal).Type() == val.Type() { + dataVal = indirectRoot(dataVal) } if !dataVal.IsValid() { @@ -727,7 +856,7 @@ func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error { } func (d *Decoder) decodeString(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) dataKind := getKind(dataVal) converted := true @@ -780,7 +909,7 @@ func (d *Decoder) decodeString(name string, data any, val reflect.Value) error { } func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() @@ -835,7 +964,7 @@ func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error { } func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() @@ -906,7 +1035,7 @@ func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error { } func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) dataKind := getKind(dataVal) switch { @@ -942,7 +1071,7 @@ func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error { } func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() @@ -997,7 +1126,7 @@ func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error { } func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) dataKind := getKind(dataVal) switch { @@ -1013,7 +1142,12 @@ func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error return nil } -func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error { +func (d *Decoder) decodeMap(name string, data any, val reflect.Value, s state) error { + if val.Kind() == reflect.Interface && s.Deep && s.ValueType != nil { + val.Set(s.makeValueType()) + val = val.Elem() + } + valType := val.Type() valKeyType := valType.Key() valElemType := valType.Elem() @@ -1032,20 +1166,20 @@ func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error { // Resolve any levels of indirection for dataVal.Kind() == reflect.Pointer { - dataVal = reflect.Indirect(dataVal) + dataVal = indirectRoot(dataVal) } // Check input type and based on the input type jump to the proper func switch dataVal.Kind() { case reflect.Map: - return d.decodeMapFromMap(name, dataVal, val, valMap) + return d.decodeMapFromMap(name, dataVal, val, valMap, s) case reflect.Struct: - return d.decodeMapFromStruct(name, dataVal, val, valMap) + return d.decodeMapFromStruct(name, dataVal, val, valMap, s) case reflect.Array, reflect.Slice: if d.config.WeaklyTypedInput { - return d.decodeMapFromSlice(name, dataVal, val, valMap) + return d.decodeMapFromSlice(name, dataVal, val, valMap, s) } fallthrough @@ -1058,7 +1192,7 @@ func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error { } } -func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { +func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value, s state) error { // Special case for BC reasons (covered by tests) if dataVal.Len() == 0 { val.Set(valMap) @@ -1068,7 +1202,10 @@ func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val ref for i := 0; i < dataVal.Len(); i++ { err := d.decode( name+"["+strconv.Itoa(i)+"]", - dataVal.Index(i).Interface(), val) + dataVal.Index(i).Interface(), + val, + s, + ) if err != nil { return err } @@ -1077,7 +1214,7 @@ func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val ref return nil } -func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { +func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value, s state) error { valType := val.Type() valKeyType := valType.Key() valElemType := valType.Elem() @@ -1103,16 +1240,24 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle fieldName := name + "[" + k.String() + "]" // First decode the key into the proper type - currentKey := reflect.Indirect(reflect.New(valKeyType)) - if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { + currentKey := indirectRoot(reflect.New(valKeyType)) + if err := d.decode(fieldName, k.Interface(), currentKey, s); err != nil { errs = append(errs, err) continue } // Next decode the data into the proper type v := dataVal.MapIndex(k).Interface() - currentVal := reflect.Indirect(reflect.New(valElemType)) - if err := d.decode(fieldName, v, currentVal); err != nil { + + var currentVal reflect.Value + + if valElemType.Kind() == reflect.Interface && s.Deep && s.ValueType != nil { + currentVal = s.makeValueType() + } else { + currentVal = reflect.Indirect(reflect.New(valElemType)) + } + + if err := d.decode(fieldName, v, currentVal, s); err != nil { errs = append(errs, err) continue } @@ -1126,7 +1271,7 @@ func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val refle return errors.Join(errs...) } -func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { +func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value, s state) error { typ := dataVal.Type() for i := 0; i < typ.NumField(); i++ { // Get the StructField first since this is a cheap operation. If the @@ -1156,9 +1301,6 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re // If Squash is set in the config, we squash the field down. squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous - // If Deep is set in the config, set as default value. - deep := d.config.Deep - v = dereferencePtrToStructIfNeeded(v, d.config.TagName) // Determine the name of the key in the map @@ -1208,7 +1350,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re } } - deep = deep || strings.Contains(tagValue[index+1:], "deep") + s.Deep = s.Deep || strings.Contains(tagValue[index+1:], "deep") + + s.Indirect = s.Indirect || strings.Contains(tagValue[index+1:], "indirect") if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { keyName = keyNameTagValue @@ -1220,9 +1364,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re keyName = tagValue } - switch v.Kind() { - // this is an embedded struct, so handle it differently - case reflect.Struct: + if v.Kind() == reflect.Struct { x := reflect.New(v.Type()) x.Elem().Set(v) @@ -1239,14 +1381,14 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re addrVal := reflect.New(vMap.Type()) reflect.Indirect(addrVal).Set(vMap) - err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal)) + err := d.decode(keyName, x.Interface(), indirectRoot(addrVal), s) 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) + vMap = indirectRoot(addrVal) if squash { for _, k := range vMap.MapKeys() { @@ -1255,44 +1397,33 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re } else { valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) } + } else { + rootV := indirectRoot(v) - case reflect.Slice: - if deep { - var childType reflect.Type - switch v.Type().Elem().Kind() { - case reflect.Struct: - childType = reflect.TypeOf(map[string]any{}) - default: - childType = v.Type().Elem() - } - - sType := reflect.SliceOf(childType) - - addrVal := reflect.New(sType) - - vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap()) + switch rootV.Kind() { + case reflect.Slice, reflect.Array, reflect.Map: + if s.Deep { + s = s.deepType(rootV.Type()) - if v.Len() > 0 { - reflect.Indirect(addrVal).Set(vSlice) + vSlice := s.makeValueType() - err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal)) - if err != nil { - return err + if rootV.Len() > 0 { + err := d.decode(keyName, rootV.Interface(), vSlice, s) + if err != nil { + return err + } } - } - vSlice = reflect.Indirect(addrVal) + valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice) - valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice) + break + } - break + // When deep mapping is not needed, fallthrough to normal copy + fallthrough + default: + valMap.SetMapIndex(reflect.ValueOf(keyName), v) } - - // When deep mapping is not needed, fallthrough to normal copy - fallthrough - - default: - valMap.SetMapIndex(reflect.ValueOf(keyName), v) } } @@ -1303,12 +1434,12 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re return nil } -func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, error) { +func (d *Decoder) decodePtr(name string, data any, val reflect.Value, s state) (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() { + switch v := indirectRoot(reflect.ValueOf(data)); v.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, @@ -1337,13 +1468,13 @@ func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, err realVal = reflect.New(valElemType) } - if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { + if err := d.decode(name, data, reflect.Indirect(realVal), s); err != nil { return false, err } val.Set(realVal) } else { - if err := d.decode(name, data, reflect.Indirect(val)); err != nil { + if err := d.decode(name, data, reflect.Indirect(val), s); err != nil { return false, err } } @@ -1353,7 +1484,7 @@ func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, err func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error { // Create an element of the concrete (non pointer) type and decode // into that. Then set the value of the pointer to this type. - dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataVal := indirectRoot(reflect.ValueOf(data)) if val.Type() != dataVal.Type() { return newDecodeError(name, &UnconvertibleTypeError{ Expected: val, @@ -1364,8 +1495,8 @@ func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error { return nil } -func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) +func (d *Decoder) decodeSlice(name string, data any, val reflect.Value, s state) error { + dataVal := indirectRoot(reflect.ValueOf(data)) dataValKind := dataVal.Kind() valType := val.Type() valElemType := valType.Elem() @@ -1386,16 +1517,16 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { return nil } // Create slice of maps of other sizes - return d.decodeSlice(name, []any{data}, val) + return d.decodeSlice(name, []any{data}, val, s) case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: - return d.decodeSlice(name, []byte(dataVal.String()), val) + return d.decodeSlice(name, []byte(dataVal.String()), val, s) // All other types we try to convert to the slice type // and "lift" it into it. i.e. a string becomes a string slice. default: // Just re-try this function with data as a slice. - return d.decodeSlice(name, []any{data}, val) + return d.decodeSlice(name, []any{data}, val, s) } } @@ -1427,7 +1558,7 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { currentField := valSlice.Index(i) fieldName := name + "[" + strconv.Itoa(i) + "]" - if err := d.decode(fieldName, currentData, currentField); err != nil { + if err := d.decode(fieldName, currentData, currentField, s); err != nil { errs = append(errs, err) } } @@ -1438,8 +1569,8 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { return errors.Join(errs...) } -func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) +func (d *Decoder) decodeArray(name string, data any, val reflect.Value, s state) error { + dataVal := indirectRoot(reflect.ValueOf(data)) dataValKind := dataVal.Kind() valType := val.Type() valElemType := valType.Elem() @@ -1463,7 +1594,7 @@ func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error { // and "lift" it into it. i.e. a string becomes a string array. default: // Just re-try this function with data as a slice. - return d.decodeArray(name, []any{data}, val) + return d.decodeArray(name, []any{data}, val, s) } } @@ -1488,7 +1619,7 @@ func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error { currentField := valArray.Index(i) fieldName := name + "[" + strconv.Itoa(i) + "]" - if err := d.decode(fieldName, currentData, currentField); err != nil { + if err := d.decode(fieldName, currentData, currentField, s); err != nil { errs = append(errs, err) } } @@ -1499,8 +1630,8 @@ func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error { return errors.Join(errs...) } -func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error { - dataVal := reflect.Indirect(reflect.ValueOf(data)) +func (d *Decoder) decodeStruct(name string, data any, val reflect.Value, s state) error { + dataVal := indirectRoot(reflect.ValueOf(data)) // If the type of the value to write to and the data match directly, // then we just set it directly instead of recursing into the structure. @@ -1512,7 +1643,7 @@ func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error { dataValKind := dataVal.Kind() switch dataValKind { case reflect.Map: - return d.decodeStructFromMap(name, dataVal, val) + return d.decodeStructFromMap(name, dataVal, val, s) case reflect.Struct: // Not the most efficient way to do this but we can optimize later if @@ -1530,11 +1661,11 @@ func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error { addrVal := reflect.New(mval.Type()) reflect.Indirect(addrVal).Set(mval) - if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil { + if err := d.decodeMapFromStruct(name, dataVal, indirectRoot(addrVal), mval, s); err != nil { return err } - result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val) + result := d.decodeStructFromMap(name, indirectRoot(addrVal), val, s) return result default: @@ -1543,7 +1674,7 @@ func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error { } } -func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { +func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value, s state) error { dataValType := dataVal.Type() if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { return newDecodeError(name, @@ -1718,7 +1849,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e fieldName = name + "." + fieldName } - if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { + if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue, s); err != nil { errs = append(errs, err) } } @@ -1733,7 +1864,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } // Decode it as-if we were just decoding this map onto our map. - if err := d.decodeMap(name, remain, remainField.val); err != nil { + if err := d.decodeMap(name, remain, remainField.val, s); err != nil { errs = append(errs, err) } diff --git a/mapstructure_test.go b/mapstructure_test.go index baf40dfe..a6e1e92b 100644 --- a/mapstructure_test.go +++ b/mapstructure_test.go @@ -3859,45 +3859,792 @@ func TestDecoder_ErrorUnused_EmbeddedStruct_QualifiedTypeName(t *testing.T) { func TestDecode_structArrayDeepMap(t *testing.T) { type SourceChild struct { - String string `mapstructure:"some-string"` + String string `mapstructure:"field"` } type SourceParent struct { - ChildrenA []SourceChild `mapstructure:"children-a,deep"` - ChildrenB *[]SourceChild `mapstructure:"children-b,deep"` + SE []SourceChild `mapstructure:"s-e,deep"` + SPE *[]SourceChild `mapstructure:"s-p-e,deep"` + SPPE **[]SourceChild `mapstructure:"s-p-p-e,deep"` + SEP []*SourceChild `mapstructure:"s-e-p,deep"` + SPEP *[]*SourceChild `mapstructure:"s-p-e-p,deep"` + SPPEP **[]*SourceChild `mapstructure:"s-p-p-e-p,deep"` + SEPP []**SourceChild `mapstructure:"s-e-p-p,deep"` + SPEPP *[]**SourceChild `mapstructure:"s-p-e-p-p,deep"` + SPPEPP **[]**SourceChild `mapstructure:"s-p-p-e-p-p,deep"` + AE [1]SourceChild `mapstructure:"a-e,deep"` + APE *[1]SourceChild `mapstructure:"a-p-e,deep"` + APPE **[1]SourceChild `mapstructure:"a-p-p-e,deep"` + AEP [1]*SourceChild `mapstructure:"a-e-p,deep"` + APEP *[1]*SourceChild `mapstructure:"a-p-e-p,deep"` + APPEP **[1]*SourceChild `mapstructure:"a-p-p-e-p,deep"` + AEPP [1]**SourceChild `mapstructure:"a-e-p-p,deep"` + APEPP *[1]**SourceChild `mapstructure:"a-p-e-p-p,deep"` + APPEPP **[1]**SourceChild `mapstructure:"a-p-p-e-p-p,deep"` + ME map[string]SourceChild `mapstructure:"m-e,deep"` + MPE *map[string]SourceChild `mapstructure:"m-p-e,deep"` + MPPE **map[string]SourceChild `mapstructure:"m-p-p-e,deep"` + MEP map[string]*SourceChild `mapstructure:"m-e-p,deep"` + MPEP *map[string]*SourceChild `mapstructure:"m-p-e-p,deep"` + MPPEP **map[string]*SourceChild `mapstructure:"m-p-p-e-p,deep"` + MEPP map[string]**SourceChild `mapstructure:"m-e-p-p,deep"` + MPEPP *map[string]**SourceChild `mapstructure:"m-p-e-p-p,deep"` + MPPEPP **map[string]**SourceChild `mapstructure:"m-p-p-e-p-p,deep"` + MSE map[string][]SourceChild `mapstructure:"m-s-e,deep"` + MPSE *map[string][]SourceChild `mapstructure:"m-p-s-e,deep"` + MPPSE **map[string][]SourceChild `mapstructure:"m-p-p-s-e,deep"` + MSPE map[string]*[]SourceChild `mapstructure:"m-s-p-e,deep"` + MPSPE *map[string]*[]SourceChild `mapstructure:"m-p-s-p-e,deep"` + MPPSPE **map[string]*[]SourceChild `mapstructure:"m-p-p-s-p-e,deep"` + MSPPE map[string]**[]SourceChild `mapstructure:"m-s-p-p-e,deep"` + MPSPPE *map[string]**[]SourceChild `mapstructure:"m-p-s-p-p-e,deep"` + MPPSPPE **map[string]**[]SourceChild `mapstructure:"m-p-p-s-p-p-e,deep"` + MAE map[string][1]SourceChild `mapstructure:"m-a-e,deep"` + MPAE *map[string][1]SourceChild `mapstructure:"m-p-a-e,deep"` + MPPAE **map[string][1]SourceChild `mapstructure:"m-p-p-a-e,deep"` + MAPE map[string]*[1]SourceChild `mapstructure:"m-a-p-e,deep"` + MPAPE *map[string]*[1]SourceChild `mapstructure:"m-p-a-p-e,deep"` + MPPAPE **map[string]*[1]SourceChild `mapstructure:"m-p-p-a-p-e,deep"` + MAPPE map[string]**[1]SourceChild `mapstructure:"m-a-p-p-e,deep"` + MPAPPE *map[string]**[1]SourceChild `mapstructure:"m-p-a-p-p-e,deep"` + MPPAPPE **map[string]**[1]SourceChild `mapstructure:"m-p-p-a-p-p-e,deep"` + MSEP map[string][]*SourceChild `mapstructure:"m-s-e-p,deep"` + MPSEP *map[string][]*SourceChild `mapstructure:"m-p-s-e-p,deep"` + MPPSEP **map[string][]*SourceChild `mapstructure:"m-p-p-s-e-p,deep"` + MSPEP map[string]*[]*SourceChild `mapstructure:"m-s-p-e-p,deep"` + MPSPEP *map[string]*[]*SourceChild `mapstructure:"m-p-s-p-e-p,deep"` + MPPSPEP **map[string]*[]*SourceChild `mapstructure:"m-p-p-s-p-e-p,deep"` + MSPPEP map[string]**[]*SourceChild `mapstructure:"m-s-p-p-e-p,deep"` + MPSPPEP *map[string]**[]*SourceChild `mapstructure:"m-p-s-p-p-e-p,deep"` + MPPSPPEP **map[string]**[]*SourceChild `mapstructure:"m-p-p-s-p-p-e-p,deep"` + MAEP map[string][1]*SourceChild `mapstructure:"m-a-e-p,deep"` + MPAEP *map[string][1]*SourceChild `mapstructure:"m-p-a-e-p,deep"` + MPPAEP **map[string][1]*SourceChild `mapstructure:"m-p-p-a-e-p,deep"` + MAPEP map[string]*[1]*SourceChild `mapstructure:"m-a-p-e-p,deep"` + MPAPEP *map[string]*[1]*SourceChild `mapstructure:"m-p-a-p-e-p,deep"` + MPPAPEP **map[string]*[1]*SourceChild `mapstructure:"m-p-p-a-p-e-p,deep"` + MAPPEP map[string]**[1]*SourceChild `mapstructure:"m-a-p-p-e-p,deep"` + MPAPPEP *map[string]**[1]*SourceChild `mapstructure:"m-p-a-p-p-e-p,deep"` + MPPAPPEP **map[string]**[1]*SourceChild `mapstructure:"m-p-p-a-p-p-e-p,deep"` + MSEPP map[string][]**SourceChild `mapstructure:"m-s-e-p-p,deep"` + MPSEPP *map[string][]**SourceChild `mapstructure:"m-p-s-e-p-p,deep"` + MPPSEPP **map[string][]**SourceChild `mapstructure:"m-p-p-s-e-p-p,deep"` + MSPEPP map[string]*[]**SourceChild `mapstructure:"m-s-p-e-p-p,deep"` + MPSPEPP *map[string]*[]**SourceChild `mapstructure:"m-p-s-p-e-p-p,deep"` + MPPSPEPP **map[string]*[]**SourceChild `mapstructure:"m-p-p-s-p-e-p-p,deep"` + MSPPEPP map[string]**[]**SourceChild `mapstructure:"m-s-p-p-e-p-p,deep"` + MPSPPEPP *map[string]**[]**SourceChild `mapstructure:"m-p-s-p-p-e-p-p,deep"` + MPPSPPEPP **map[string]**[]**SourceChild `mapstructure:"m-p-p-s-p-p-e-p-p,deep"` + MAEPP map[string][1]**SourceChild `mapstructure:"m-a-e-p-p,deep"` + MPAEPP *map[string][1]**SourceChild `mapstructure:"m-p-a-e-p-p,deep"` + MPPAEPP **map[string][1]**SourceChild `mapstructure:"m-p-p-a-e-p-p,deep"` + MAPEPP map[string]*[1]**SourceChild `mapstructure:"m-a-p-e-p-p,deep"` + MPAPEPP *map[string]*[1]**SourceChild `mapstructure:"m-p-a-p-e-p-p,deep"` + MPPAPEPP **map[string]*[1]**SourceChild `mapstructure:"m-p-p-a-p-e-p-p,deep"` + MAPPEPP map[string]**[1]**SourceChild `mapstructure:"m-a-p-p-e-p-p,deep"` + MPAPPEPP *map[string]**[1]**SourceChild `mapstructure:"m-p-a-p-p-e-p-p,deep"` + MPPAPPEPP **map[string]**[1]**SourceChild `mapstructure:"m-p-p-a-p-p-e-p-p,deep"` + } + + e := SourceChild{String: "el"} + ep := &e + epp := &ep + + se := []SourceChild{e} + spe := &se + sppe := &spe + + sep := []*SourceChild{ep} + spep := &sep + sppep := &spep + + sepp := []**SourceChild{epp} + spepp := &sepp + sppepp := &spepp + + ae := [1]SourceChild{e} + ape := &ae + appe := &ape + + aep := [1]*SourceChild{ep} + apep := &aep + appep := &apep + + aepp := [1]**SourceChild{epp} + apepp := &aepp + appepp := &apepp + + me := map[string]SourceChild{"foo": e} + mpe := &me + mppe := &mpe + + mep := map[string]*SourceChild{"foo": ep} + mpep := &mep + mppep := &mpep + + mepp := map[string]**SourceChild{"foo": epp} + mpepp := &mepp + mppepp := &mpepp + + mse := map[string][]SourceChild{"foo": se} + mpse := &mse + mppse := &mpse + + mspe := map[string]*[]SourceChild{"foo": spe} + mpspe := &mspe + mppspe := &mpspe + + msppe := map[string]**[]SourceChild{"foo": sppe} + mpsppe := &msppe + mppsppe := &mpsppe + + mae := map[string][1]SourceChild{"foo": ae} + mpae := &mae + mppae := &mpae + + mape := map[string]*[1]SourceChild{"foo": ape} + mpape := &mape + mppape := &mpape + + mappe := map[string]**[1]SourceChild{"foo": appe} + mpappe := &mappe + mppappe := &mpappe + + msep := map[string][]*SourceChild{"foo": sep} + mpsep := &msep + mppsep := &mpsep + + mspep := map[string]*[]*SourceChild{"foo": spep} + mpspep := &mspep + mppspep := &mpspep + + msppep := map[string]**[]*SourceChild{"foo": sppep} + mpsppep := &msppep + mppsppep := &mpsppep + + maep := map[string][1]*SourceChild{"foo": aep} + mpaep := &maep + mppaep := &mpaep + + mapep := map[string]*[1]*SourceChild{"foo": apep} + mpapep := &mapep + mppapep := &mpapep + + mappep := map[string]**[1]*SourceChild{"foo": appep} + mpappep := &mappep + mppappep := &mpappep + + msepp := map[string][]**SourceChild{"foo": sepp} + mpsepp := &msepp + mppsepp := &mpsepp + + mspepp := map[string]*[]**SourceChild{"foo": spepp} + mpspepp := &mspepp + mppspepp := &mpspepp + + msppepp := map[string]**[]**SourceChild{"foo": sppepp} + mpsppepp := &msppepp + mppsppepp := &mpsppepp + + maepp := map[string][1]**SourceChild{"foo": aepp} + mpaepp := &maepp + mppaepp := &mpaepp + + mapepp := map[string]*[1]**SourceChild{"foo": apepp} + mpapepp := &mapepp + mppapepp := &mpapepp + + mappepp := map[string]**[1]**SourceChild{"foo": appepp} + mpappepp := &mappepp + mppappepp := &mpappepp + + var target map[string]any + + source := SourceParent{ + SE: se, + SPE: spe, + SPPE: sppe, + SEP: sep, + SPEP: spep, + SPPEP: sppep, + SEPP: sepp, + SPEPP: spepp, + SPPEPP: sppepp, + AE: ae, + APE: ape, + APPE: appe, + AEP: aep, + APEP: apep, + APPEP: appep, + AEPP: aepp, + APEPP: apepp, + APPEPP: appepp, + ME: me, + MPE: mpe, + MPPE: mppe, + MEP: mep, + MPEP: mpep, + MPPEP: mppep, + MEPP: mepp, + MPEPP: mpepp, + MPPEPP: mppepp, + MSE: mse, + MPSE: mpse, + MPPSE: mppse, + MSPE: mspe, + MPSPE: mpspe, + MPPSPE: mppspe, + MSPPE: msppe, + MPSPPE: mpsppe, + MPPSPPE: mppsppe, + MAE: mae, + MPAE: mpae, + MPPAE: mppae, + MAPE: mape, + MPAPE: mpape, + MPPAPE: mppape, + MAPPE: mappe, + MPAPPE: mpappe, + MPPAPPE: mppappe, + MSEP: msep, + MPSEP: mpsep, + MPPSEP: mppsep, + MSPEP: mspep, + MPSPEP: mpspep, + MPPSPEP: mppspep, + MSPPEP: msppep, + MPSPPEP: mpsppep, + MPPSPPEP: mppsppep, + MAEP: maep, + MPAEP: mpaep, + MPPAEP: mppaep, + MAPEP: mapep, + MPAPEP: mpapep, + MPPAPEP: mppapep, + MAPPEP: mappep, + MPAPPEP: mpappep, + MPPAPPEP: mppappep, + MSEPP: msepp, + MPSEPP: mpsepp, + MPPSEPP: mppsepp, + MSPEPP: mspepp, + MPSPEPP: mpspepp, + MPPSPEPP: mppspepp, + MSPPEPP: msppepp, + MPSPPEPP: mpsppepp, + MPPSPPEPP: mppsppepp, + MAEPP: maepp, + MPAEPP: mpaepp, + MPPAEPP: mppaepp, + MAPEPP: mapepp, + MPAPEPP: mpapepp, + MPPAPEPP: mppapepp, + MAPPEPP: mappepp, + MPAPPEPP: mpappepp, + MPPAPPEPP: mppappepp, + } + + if err := Decode(source, &target); err != nil { + t.Fatalf("got error: %s", err) + } + + sliceValue := []map[string]any{{"field": "el"}} + arrayValue := [1]map[string]any{{"field": "el"}} + mapValue := map[string]map[string]any{"foo": {"field": "el"}} + mapSliceValue := map[string][]map[string]any{"foo": {{"field": "el"}}} + mapArrayValue := map[string][1]map[string]any{"foo": {{"field": "el"}}} + + expected := map[string]any{ + "s-e": sliceValue, + "s-p-e": sliceValue, + "s-p-p-e": sliceValue, + "s-e-p": sliceValue, + "s-p-e-p": sliceValue, + "s-p-p-e-p": sliceValue, + "s-e-p-p": sliceValue, + "s-p-e-p-p": sliceValue, + "s-p-p-e-p-p": sliceValue, + "a-e": arrayValue, + "a-p-e": arrayValue, + "a-p-p-e": arrayValue, + "a-e-p": arrayValue, + "a-p-e-p": arrayValue, + "a-p-p-e-p": arrayValue, + "a-e-p-p": arrayValue, + "a-p-e-p-p": arrayValue, + "a-p-p-e-p-p": arrayValue, + "m-e": mapValue, + "m-p-e": mapValue, + "m-p-p-e": mapValue, + "m-e-p": mapValue, + "m-p-e-p": mapValue, + "m-p-p-e-p": mapValue, + "m-e-p-p": mapValue, + "m-p-e-p-p": mapValue, + "m-p-p-e-p-p": mapValue, + "m-s-e": mapSliceValue, + "m-p-s-e": mapSliceValue, + "m-p-p-s-e": mapSliceValue, + "m-s-p-e": mapSliceValue, + "m-p-s-p-e": mapSliceValue, + "m-p-p-s-p-e": mapSliceValue, + "m-s-p-p-e": mapSliceValue, + "m-p-s-p-p-e": mapSliceValue, + "m-p-p-s-p-p-e": mapSliceValue, + "m-a-e": mapArrayValue, + "m-p-a-e": mapArrayValue, + "m-p-p-a-e": mapArrayValue, + "m-a-p-e": mapArrayValue, + "m-p-a-p-e": mapArrayValue, + "m-p-p-a-p-e": mapArrayValue, + "m-a-p-p-e": mapArrayValue, + "m-p-a-p-p-e": mapArrayValue, + "m-p-p-a-p-p-e": mapArrayValue, + "m-s-e-p": mapSliceValue, + "m-p-s-e-p": mapSliceValue, + "m-p-p-s-e-p": mapSliceValue, + "m-s-p-e-p": mapSliceValue, + "m-p-s-p-e-p": mapSliceValue, + "m-p-p-s-p-e-p": mapSliceValue, + "m-s-p-p-e-p": mapSliceValue, + "m-p-s-p-p-e-p": mapSliceValue, + "m-p-p-s-p-p-e-p": mapSliceValue, + "m-a-e-p": mapArrayValue, + "m-p-a-e-p": mapArrayValue, + "m-p-p-a-e-p": mapArrayValue, + "m-a-p-e-p": mapArrayValue, + "m-p-a-p-e-p": mapArrayValue, + "m-p-p-a-p-e-p": mapArrayValue, + "m-a-p-p-e-p": mapArrayValue, + "m-p-a-p-p-e-p": mapArrayValue, + "m-p-p-a-p-p-e-p": mapArrayValue, + "m-s-e-p-p": mapSliceValue, + "m-p-s-e-p-p": mapSliceValue, + "m-p-p-s-e-p-p": mapSliceValue, + "m-s-p-e-p-p": mapSliceValue, + "m-p-s-p-e-p-p": mapSliceValue, + "m-p-p-s-p-e-p-p": mapSliceValue, + "m-s-p-p-e-p-p": mapSliceValue, + "m-p-s-p-p-e-p-p": mapSliceValue, + "m-p-p-s-p-p-e-p-p": mapSliceValue, + "m-a-e-p-p": mapArrayValue, + "m-p-a-e-p-p": mapArrayValue, + "m-p-p-a-e-p-p": mapArrayValue, + "m-a-p-e-p-p": mapArrayValue, + "m-p-a-p-e-p-p": mapArrayValue, + "m-p-p-a-p-e-p-p": mapArrayValue, + "m-a-p-p-e-p-p": mapArrayValue, + "m-p-a-p-p-e-p-p": mapArrayValue, + "m-p-p-a-p-p-e-p-p": mapArrayValue, + } + + if !reflect.DeepEqual(target, expected) { + t.Fatalf("failed: \nexpected: %#v\nresult: %#v", expected, target) + } + + var reverse SourceParent + + if err := Decode(target, &reverse); err != nil { + t.Fatalf("got error: %s", err) } + if !reflect.DeepEqual(source, reverse) { + t.Fatalf("failed: \nexpected: %#v\nresult: %#v", source, reverse) + } +} + +func TestDecode_structArrayDeepMapIndirect(t *testing.T) { + type SourceChild struct { + String string `mapstructure:"field"` + } + + type SourceParent struct { + SE []SourceChild `mapstructure:"s-e,deep,indirect"` + SPE *[]SourceChild `mapstructure:"s-p-e,deep,indirect"` + SPPE **[]SourceChild `mapstructure:"s-p-p-e,deep,indirect"` + SEP []*SourceChild `mapstructure:"s-e-p,deep,indirect"` + SPEP *[]*SourceChild `mapstructure:"s-p-e-p,deep,indirect"` + SPPEP **[]*SourceChild `mapstructure:"s-p-p-e-p,deep,indirect"` + SEPP []**SourceChild `mapstructure:"s-e-p-p,deep,indirect"` + SPEPP *[]**SourceChild `mapstructure:"s-p-e-p-p,deep,indirect"` + SPPEPP **[]**SourceChild `mapstructure:"s-p-p-e-p-p,deep,indirect"` + AE [1]SourceChild `mapstructure:"a-e,deep,indirect"` + APE *[1]SourceChild `mapstructure:"a-p-e,deep,indirect"` + APPE **[1]SourceChild `mapstructure:"a-p-p-e,deep,indirect"` + AEP [1]*SourceChild `mapstructure:"a-e-p,deep,indirect"` + APEP *[1]*SourceChild `mapstructure:"a-p-e-p,deep,indirect"` + APPEP **[1]*SourceChild `mapstructure:"a-p-p-e-p,deep,indirect"` + AEPP [1]**SourceChild `mapstructure:"a-e-p-p,deep,indirect"` + APEPP *[1]**SourceChild `mapstructure:"a-p-e-p-p,deep,indirect"` + APPEPP **[1]**SourceChild `mapstructure:"a-p-p-e-p-p,deep,indirect"` + ME map[string]SourceChild `mapstructure:"m-e,deep,indirect"` + MPE *map[string]SourceChild `mapstructure:"m-p-e,deep,indirect"` + MPPE **map[string]SourceChild `mapstructure:"m-p-p-e,deep,indirect"` + MEP map[string]*SourceChild `mapstructure:"m-e-p,deep,indirect"` + MPEP *map[string]*SourceChild `mapstructure:"m-p-e-p,deep,indirect"` + MPPEP **map[string]*SourceChild `mapstructure:"m-p-p-e-p,deep,indirect"` + MEPP map[string]**SourceChild `mapstructure:"m-e-p-p,deep,indirect"` + MPEPP *map[string]**SourceChild `mapstructure:"m-p-e-p-p,deep,indirect"` + MPPEPP **map[string]**SourceChild `mapstructure:"m-p-p-e-p-p,deep,indirect"` + MSE map[string][]SourceChild `mapstructure:"m-s-e,deep,indirect"` + MPSE *map[string][]SourceChild `mapstructure:"m-p-s-e,deep,indirect"` + MPPSE **map[string][]SourceChild `mapstructure:"m-p-p-s-e,deep,indirect"` + MSPE map[string]*[]SourceChild `mapstructure:"m-s-p-e,deep,indirect"` + MPSPE *map[string]*[]SourceChild `mapstructure:"m-p-s-p-e,deep,indirect"` + MPPSPE **map[string]*[]SourceChild `mapstructure:"m-p-p-s-p-e,deep,indirect"` + MSPPE map[string]**[]SourceChild `mapstructure:"m-s-p-p-e,deep,indirect"` + MPSPPE *map[string]**[]SourceChild `mapstructure:"m-p-s-p-p-e,deep,indirect"` + MPPSPPE **map[string]**[]SourceChild `mapstructure:"m-p-p-s-p-p-e,deep,indirect"` + MAE map[string][1]SourceChild `mapstructure:"m-a-e,deep,indirect"` + MPAE *map[string][1]SourceChild `mapstructure:"m-p-a-e,deep,indirect"` + MPPAE **map[string][1]SourceChild `mapstructure:"m-p-p-a-e,deep,indirect"` + MAPE map[string]*[1]SourceChild `mapstructure:"m-a-p-e,deep,indirect"` + MPAPE *map[string]*[1]SourceChild `mapstructure:"m-p-a-p-e,deep,indirect"` + MPPAPE **map[string]*[1]SourceChild `mapstructure:"m-p-p-a-p-e,deep,indirect"` + MAPPE map[string]**[1]SourceChild `mapstructure:"m-a-p-p-e,deep,indirect"` + MPAPPE *map[string]**[1]SourceChild `mapstructure:"m-p-a-p-p-e,deep,indirect"` + MPPAPPE **map[string]**[1]SourceChild `mapstructure:"m-p-p-a-p-p-e,deep,indirect"` + MSEP map[string][]*SourceChild `mapstructure:"m-s-e-p,deep,indirect"` + MPSEP *map[string][]*SourceChild `mapstructure:"m-p-s-e-p,deep,indirect"` + MPPSEP **map[string][]*SourceChild `mapstructure:"m-p-p-s-e-p,deep,indirect"` + MSPEP map[string]*[]*SourceChild `mapstructure:"m-s-p-e-p,deep,indirect"` + MPSPEP *map[string]*[]*SourceChild `mapstructure:"m-p-s-p-e-p,deep,indirect"` + MPPSPEP **map[string]*[]*SourceChild `mapstructure:"m-p-p-s-p-e-p,deep,indirect"` + MSPPEP map[string]**[]*SourceChild `mapstructure:"m-s-p-p-e-p,deep,indirect"` + MPSPPEP *map[string]**[]*SourceChild `mapstructure:"m-p-s-p-p-e-p,deep,indirect"` + MPPSPPEP **map[string]**[]*SourceChild `mapstructure:"m-p-p-s-p-p-e-p,deep,indirect"` + MAEP map[string][1]*SourceChild `mapstructure:"m-a-e-p,deep,indirect"` + MPAEP *map[string][1]*SourceChild `mapstructure:"m-p-a-e-p,deep,indirect"` + MPPAEP **map[string][1]*SourceChild `mapstructure:"m-p-p-a-e-p,deep,indirect"` + MAPEP map[string]*[1]*SourceChild `mapstructure:"m-a-p-e-p,deep,indirect"` + MPAPEP *map[string]*[1]*SourceChild `mapstructure:"m-p-a-p-e-p,deep,indirect"` + MPPAPEP **map[string]*[1]*SourceChild `mapstructure:"m-p-p-a-p-e-p,deep,indirect"` + MAPPEP map[string]**[1]*SourceChild `mapstructure:"m-a-p-p-e-p,deep,indirect"` + MPAPPEP *map[string]**[1]*SourceChild `mapstructure:"m-p-a-p-p-e-p,deep,indirect"` + MPPAPPEP **map[string]**[1]*SourceChild `mapstructure:"m-p-p-a-p-p-e-p,deep,indirect"` + MSEPP map[string][]**SourceChild `mapstructure:"m-s-e-p-p,deep,indirect"` + MPSEPP *map[string][]**SourceChild `mapstructure:"m-p-s-e-p-p,deep,indirect"` + MPPSEPP **map[string][]**SourceChild `mapstructure:"m-p-p-s-e-p-p,deep,indirect"` + MSPEPP map[string]*[]**SourceChild `mapstructure:"m-s-p-e-p-p,deep,indirect"` + MPSPEPP *map[string]*[]**SourceChild `mapstructure:"m-p-s-p-e-p-p,deep,indirect"` + MPPSPEPP **map[string]*[]**SourceChild `mapstructure:"m-p-p-s-p-e-p-p,deep,indirect"` + MSPPEPP map[string]**[]**SourceChild `mapstructure:"m-s-p-p-e-p-p,deep,indirect"` + MPSPPEPP *map[string]**[]**SourceChild `mapstructure:"m-p-s-p-p-e-p-p,deep,indirect"` + MPPSPPEPP **map[string]**[]**SourceChild `mapstructure:"m-p-p-s-p-p-e-p-p,deep,indirect"` + MAEPP map[string][1]**SourceChild `mapstructure:"m-a-e-p-p,deep,indirect"` + MPAEPP *map[string][1]**SourceChild `mapstructure:"m-p-a-e-p-p,deep,indirect"` + MPPAEPP **map[string][1]**SourceChild `mapstructure:"m-p-p-a-e-p-p,deep,indirect"` + MAPEPP map[string]*[1]**SourceChild `mapstructure:"m-a-p-e-p-p,deep,indirect"` + MPAPEPP *map[string]*[1]**SourceChild `mapstructure:"m-p-a-p-e-p-p,deep,indirect"` + MPPAPEPP **map[string]*[1]**SourceChild `mapstructure:"m-p-p-a-p-e-p-p,deep,indirect"` + MAPPEPP map[string]**[1]**SourceChild `mapstructure:"m-a-p-p-e-p-p,deep,indirect"` + MPAPPEPP *map[string]**[1]**SourceChild `mapstructure:"m-p-a-p-p-e-p-p,deep,indirect"` + MPPAPPEPP **map[string]**[1]**SourceChild `mapstructure:"m-p-p-a-p-p-e-p-p,deep,indirect"` + } + + e := SourceChild{String: "el"} + ep := &e + epp := &ep + + se := []SourceChild{e} + spe := &se + sppe := &spe + + sep := []*SourceChild{ep} + spep := &sep + sppep := &spep + + sepp := []**SourceChild{epp} + spepp := &sepp + sppepp := &spepp + + ae := [1]SourceChild{e} + ape := &ae + appe := &ape + + aep := [1]*SourceChild{ep} + apep := &aep + appep := &apep + + aepp := [1]**SourceChild{epp} + apepp := &aepp + appepp := &apepp + + me := map[string]SourceChild{"foo": e} + mpe := &me + mppe := &mpe + + mep := map[string]*SourceChild{"foo": ep} + mpep := &mep + mppep := &mpep + + mepp := map[string]**SourceChild{"foo": epp} + mpepp := &mepp + mppepp := &mpepp + + mse := map[string][]SourceChild{"foo": se} + mpse := &mse + mppse := &mpse + + mspe := map[string]*[]SourceChild{"foo": spe} + mpspe := &mspe + mppspe := &mpspe + + msppe := map[string]**[]SourceChild{"foo": sppe} + mpsppe := &msppe + mppsppe := &mpsppe + + mae := map[string][1]SourceChild{"foo": ae} + mpae := &mae + mppae := &mpae + + mape := map[string]*[1]SourceChild{"foo": ape} + mpape := &mape + mppape := &mpape + + mappe := map[string]**[1]SourceChild{"foo": appe} + mpappe := &mappe + mppappe := &mpappe + + msep := map[string][]*SourceChild{"foo": sep} + mpsep := &msep + mppsep := &mpsep + + mspep := map[string]*[]*SourceChild{"foo": spep} + mpspep := &mspep + mppspep := &mpspep + + msppep := map[string]**[]*SourceChild{"foo": sppep} + mpsppep := &msppep + mppsppep := &mpsppep + + maep := map[string][1]*SourceChild{"foo": aep} + mpaep := &maep + mppaep := &mpaep + + mapep := map[string]*[1]*SourceChild{"foo": apep} + mpapep := &mapep + mppapep := &mpapep + + mappep := map[string]**[1]*SourceChild{"foo": appep} + mpappep := &mappep + mppappep := &mpappep + + msepp := map[string][]**SourceChild{"foo": sepp} + mpsepp := &msepp + mppsepp := &mpsepp + + mspepp := map[string]*[]**SourceChild{"foo": spepp} + mpspepp := &mspepp + mppspepp := &mpspepp + + msppepp := map[string]**[]**SourceChild{"foo": sppepp} + mpsppepp := &msppepp + mppsppepp := &mpsppepp + + maepp := map[string][1]**SourceChild{"foo": aepp} + mpaepp := &maepp + mppaepp := &mpaepp + + mapepp := map[string]*[1]**SourceChild{"foo": apepp} + mpapepp := &mapepp + mppapepp := &mpapepp + + mappepp := map[string]**[1]**SourceChild{"foo": appepp} + mpappepp := &mappepp + mppappepp := &mpappepp + var target map[string]any source := SourceParent{ - ChildrenA: []SourceChild{ - {String: "one"}, - {String: "two"}, - }, - ChildrenB: &[]SourceChild{ - {String: "one"}, - {String: "two"}, - }, + SE: se, + SPE: spe, + SPPE: sppe, + SEP: sep, + SPEP: spep, + SPPEP: sppep, + SEPP: sepp, + SPEPP: spepp, + SPPEPP: sppepp, + AE: ae, + APE: ape, + APPE: appe, + AEP: aep, + APEP: apep, + APPEP: appep, + AEPP: aepp, + APEPP: apepp, + APPEPP: appepp, + ME: me, + MPE: mpe, + MPPE: mppe, + MEP: mep, + MPEP: mpep, + MPPEP: mppep, + MEPP: mepp, + MPEPP: mpepp, + MPPEPP: mppepp, + MSE: mse, + MPSE: mpse, + MPPSE: mppse, + MSPE: mspe, + MPSPE: mpspe, + MPPSPE: mppspe, + MSPPE: msppe, + MPSPPE: mpsppe, + MPPSPPE: mppsppe, + MAE: mae, + MPAE: mpae, + MPPAE: mppae, + MAPE: mape, + MPAPE: mpape, + MPPAPE: mppape, + MAPPE: mappe, + MPAPPE: mpappe, + MPPAPPE: mppappe, + MSEP: msep, + MPSEP: mpsep, + MPPSEP: mppsep, + MSPEP: mspep, + MPSPEP: mpspep, + MPPSPEP: mppspep, + MSPPEP: msppep, + MPSPPEP: mpsppep, + MPPSPPEP: mppsppep, + MAEP: maep, + MPAEP: mpaep, + MPPAEP: mppaep, + MAPEP: mapep, + MPAPEP: mpapep, + MPPAPEP: mppapep, + MAPPEP: mappep, + MPAPPEP: mpappep, + MPPAPPEP: mppappep, + MSEPP: msepp, + MPSEPP: mpsepp, + MPPSEPP: mppsepp, + MSPEPP: mspepp, + MPSPEPP: mpspepp, + MPPSPEPP: mppspepp, + MSPPEPP: msppepp, + MPSPPEPP: mpsppepp, + MPPSPPEPP: mppsppepp, + MAEPP: maepp, + MPAEPP: mpaepp, + MPPAEPP: mppaepp, + MAPEPP: mapepp, + MPAPEPP: mpapepp, + MPPAPEPP: mppapepp, + MAPPEPP: mappepp, + MPAPPEPP: mpappepp, + MPPAPPEPP: mppappepp, } if err := Decode(source, &target); err != nil { t.Fatalf("got error: %s", err) } + sliceValue := []any{map[string]any{"field": "el"}} + mapValue := map[string]any{"foo": map[string]any{"field": "el"}} + mapSliceValue := map[string]any{"foo": []any{map[string]any{"field": "el"}}} + expected := map[string]any{ - "children-a": []map[string]any{ - {"some-string": "one"}, - {"some-string": "two"}, - }, - "children-b": []map[string]any{ - {"some-string": "one"}, - {"some-string": "two"}, - }, + "s-e": sliceValue, + "s-p-e": sliceValue, + "s-p-p-e": sliceValue, + "s-e-p": sliceValue, + "s-p-e-p": sliceValue, + "s-p-p-e-p": sliceValue, + "s-e-p-p": sliceValue, + "s-p-e-p-p": sliceValue, + "s-p-p-e-p-p": sliceValue, + "a-e": sliceValue, + "a-p-e": sliceValue, + "a-p-p-e": sliceValue, + "a-e-p": sliceValue, + "a-p-e-p": sliceValue, + "a-p-p-e-p": sliceValue, + "a-e-p-p": sliceValue, + "a-p-e-p-p": sliceValue, + "a-p-p-e-p-p": sliceValue, + "m-e": mapValue, + "m-p-e": mapValue, + "m-p-p-e": mapValue, + "m-e-p": mapValue, + "m-p-e-p": mapValue, + "m-p-p-e-p": mapValue, + "m-e-p-p": mapValue, + "m-p-e-p-p": mapValue, + "m-p-p-e-p-p": mapValue, + "m-s-e": mapSliceValue, + "m-p-s-e": mapSliceValue, + "m-p-p-s-e": mapSliceValue, + "m-s-p-e": mapSliceValue, + "m-p-s-p-e": mapSliceValue, + "m-p-p-s-p-e": mapSliceValue, + "m-s-p-p-e": mapSliceValue, + "m-p-s-p-p-e": mapSliceValue, + "m-p-p-s-p-p-e": mapSliceValue, + "m-a-e": mapSliceValue, + "m-p-a-e": mapSliceValue, + "m-p-p-a-e": mapSliceValue, + "m-a-p-e": mapSliceValue, + "m-p-a-p-e": mapSliceValue, + "m-p-p-a-p-e": mapSliceValue, + "m-a-p-p-e": mapSliceValue, + "m-p-a-p-p-e": mapSliceValue, + "m-p-p-a-p-p-e": mapSliceValue, + "m-s-e-p": mapSliceValue, + "m-p-s-e-p": mapSliceValue, + "m-p-p-s-e-p": mapSliceValue, + "m-s-p-e-p": mapSliceValue, + "m-p-s-p-e-p": mapSliceValue, + "m-p-p-s-p-e-p": mapSliceValue, + "m-s-p-p-e-p": mapSliceValue, + "m-p-s-p-p-e-p": mapSliceValue, + "m-p-p-s-p-p-e-p": mapSliceValue, + "m-a-e-p": mapSliceValue, + "m-p-a-e-p": mapSliceValue, + "m-p-p-a-e-p": mapSliceValue, + "m-a-p-e-p": mapSliceValue, + "m-p-a-p-e-p": mapSliceValue, + "m-p-p-a-p-e-p": mapSliceValue, + "m-a-p-p-e-p": mapSliceValue, + "m-p-a-p-p-e-p": mapSliceValue, + "m-p-p-a-p-p-e-p": mapSliceValue, + "m-s-e-p-p": mapSliceValue, + "m-p-s-e-p-p": mapSliceValue, + "m-p-p-s-e-p-p": mapSliceValue, + "m-s-p-e-p-p": mapSliceValue, + "m-p-s-p-e-p-p": mapSliceValue, + "m-p-p-s-p-e-p-p": mapSliceValue, + "m-s-p-p-e-p-p": mapSliceValue, + "m-p-s-p-p-e-p-p": mapSliceValue, + "m-p-p-s-p-p-e-p-p": mapSliceValue, + "m-a-e-p-p": mapSliceValue, + "m-p-a-e-p-p": mapSliceValue, + "m-p-p-a-e-p-p": mapSliceValue, + "m-a-p-e-p-p": mapSliceValue, + "m-p-a-p-e-p-p": mapSliceValue, + "m-p-p-a-p-e-p-p": mapSliceValue, + "m-a-p-p-e-p-p": mapSliceValue, + "m-p-a-p-p-e-p-p": mapSliceValue, + "m-p-p-a-p-p-e-p-p": mapSliceValue, } if !reflect.DeepEqual(target, expected) { t.Fatalf("failed: \nexpected: %#v\nresult: %#v", expected, target) } + + var reverse SourceParent + + if err := Decode(target, &reverse); err != nil { + t.Fatalf("got error: %s", err) + } + + if !reflect.DeepEqual(source, reverse) { + t.Fatalf("failed: \nexpected: %#v\nresult: %#v", source, reverse) + } } func TestDecoder_RootName(t *testing.T) {