Codebase list golang-github-mitchellh-mapstructure / d5b1da7
Merge pull request #205 from RussellLuo/add-support-for-squashing-struct-ptr Add support for squashing embedded struct pointers Mitchell Hashimoto authored 3 years ago GitHub committed 3 years ago
2 changed file(s) with 90 addition(s) and 8 deletion(s). Raw diff Collapse all Expand all
861861 // Next get the actual value of this field and verify it is assignable
862862 // to the map value.
863863 v := dataVal.Field(i)
864 if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
865 // Handle embedded struct pointers as embedded structs.
866 v = v.Elem()
867 }
864868 if !v.Type().AssignableTo(valMap.Type().Elem()) {
865869 return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
866870 }
12311235
12321236 for i := 0; i < structType.NumField(); i++ {
12331237 fieldType := structType.Field(i)
1234 fieldKind := fieldType.Type.Kind()
1238 fieldVal := structVal.Field(i)
1239 if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {
1240 // Handle embedded struct pointers as embedded structs.
1241 fieldVal = fieldVal.Elem()
1242 }
12351243
12361244 // If "squash" is specified in the tag, we squash the field down.
1237 squash := d.config.Squash && fieldKind == reflect.Struct && fieldType.Anonymous
1245 squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous
12381246 remain := false
12391247
12401248 // We always parse the tags cause we're looking for other tags too
12521260 }
12531261
12541262 if squash {
1255 if fieldKind != reflect.Struct {
1263 if fieldVal.Kind() != reflect.Struct {
12561264 errors = appendErrors(errors,
1257 fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind))
1265 fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
12581266 } else {
1259 structs = append(structs, structVal.FieldByName(fieldType.Name))
1267 structs = append(structs, fieldVal)
12601268 }
12611269 continue
12621270 }
12631271
12641272 // Build our field
12651273 if remain {
1266 remainField = &field{fieldType, structVal.Field(i)}
1274 remainField = &field{fieldType, fieldVal}
12671275 } else {
12681276 // Normal struct field, store it away
1269 fields = append(fields, field{fieldType, structVal.Field(i)})
1277 fields = append(fields, field{fieldType, fieldVal})
12701278 }
12711279 }
12721280 }
5757
5858 type EmbeddedSquash struct {
5959 Basic `mapstructure:",squash"`
60 Vunique string
61 }
62
63 type EmbeddedPointerSquash struct {
64 *Basic `mapstructure:",squash"`
6065 Vunique string
6166 }
6267
654659 }
655660 }
656661
662 func TestDecode_EmbeddedPointerSquash_FromStructToMap(t *testing.T) {
663 t.Parallel()
664
665 input := EmbeddedPointerSquash{
666 Basic: &Basic{
667 Vstring: "foo",
668 },
669 Vunique: "bar",
670 }
671
672 var result map[string]interface{}
673 err := Decode(input, &result)
674 if err != nil {
675 t.Fatalf("got an err: %s", err.Error())
676 }
677
678 if result["Vstring"] != "foo" {
679 t.Errorf("vstring value should be 'foo': %#v", result["Vstring"])
680 }
681
682 if result["Vunique"] != "bar" {
683 t.Errorf("vunique value should be 'bar': %#v", result["Vunique"])
684 }
685 }
686
687 func TestDecode_EmbeddedPointerSquash_FromMapToStruct(t *testing.T) {
688 t.Parallel()
689
690 input := map[string]interface{}{
691 "Vstring": "foo",
692 "Vunique": "bar",
693 }
694
695 result := EmbeddedPointerSquash{
696 Basic: &Basic{},
697 }
698 err := Decode(input, &result)
699 if err != nil {
700 t.Fatalf("got an err: %s", err.Error())
701 }
702
703 if result.Vstring != "foo" {
704 t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
705 }
706
707 if result.Vunique != "bar" {
708 t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
709 }
710 }
711
657712 func TestDecode_EmbeddedSquashConfig(t *testing.T) {
658713 t.Parallel()
659714
23332388 "visible-map": emptyMap,
23342389 "omittable-map": map[string]interface{}{"k": "v"},
23352390 "visible-nested": emptyNested,
2336 "omittable-nested": &Nested{},
2391 "omittable-nested": map[string]interface{}{
2392 "Vbar": map[string]interface{}{
2393 "Vbool": false,
2394 "Vdata": interface{}(nil),
2395 "Vextra": "",
2396 "Vfloat": float64(0),
2397 "Vint": 0,
2398 "Vint16": int16(0),
2399 "Vint32": int32(0),
2400 "Vint64": int64(0),
2401 "Vint8": int8(0),
2402 "VjsonFloat": float64(0),
2403 "VjsonInt": 0,
2404 "VjsonNumber": json.Number(""),
2405 "VjsonUint": uint(0),
2406 "Vstring": "",
2407 "Vuint": uint(0),
2408 },
2409 "Vfoo": "",
2410 },
23372411 }
23382412
23392413 actual := &map[string]interface{}{}