Skip to content

Commit

Permalink
Adding support for squash: interface. (#17)
Browse files Browse the repository at this point in the history
* Adding support for squash: interface.

* fix lint issues.
  • Loading branch information
m1k1o authored Aug 12, 2024
1 parent 2842dc8 commit c97971d
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 3 deletions.
11 changes: 8 additions & 3 deletions mapstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -1375,10 +1375,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}

if squash {
if fieldVal.Kind() != reflect.Struct {
errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
} else {
switch fieldVal.Kind() {
case reflect.Struct:
structs = append(structs, fieldVal)
case reflect.Interface:
if !fieldVal.IsNil() {
structs = append(structs, fieldVal.Elem().Elem())
}
default:
errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
}
continue
}
Expand Down
195 changes: 195 additions & 0 deletions mapstructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,60 @@ type SquashOnNonStructType struct {
InvalidSquashType int `mapstructure:",squash"`
}

type TestInterface interface {
GetVfoo() string
GetVbarfoo() string
GetVfoobar() string
}

type TestInterfaceImpl struct {
Vfoo string
}

func (t *TestInterfaceImpl) GetVfoo() string {
return t.Vfoo
}

func (t *TestInterfaceImpl) GetVbarfoo() string {
return ""
}

func (t *TestInterfaceImpl) GetVfoobar() string {
return ""
}

type TestNestedInterfaceImpl struct {
SquashOnNestedInterfaceType `mapstructure:",squash"`
Vfoo string
}

func (t *TestNestedInterfaceImpl) GetVfoo() string {
return t.Vfoo
}

func (t *TestNestedInterfaceImpl) GetVbarfoo() string {
return t.Vbarfoo
}

func (t *TestNestedInterfaceImpl) GetVfoobar() string {
return t.NestedSquash.Vfoobar
}

type SquashOnInterfaceType struct {
TestInterface `mapstructure:",squash"`
Vbar string
}

type NestedSquash struct {
SquashOnInterfaceType `mapstructure:",squash"`
Vfoobar string
}

type SquashOnNestedInterfaceType struct {
NestedSquash NestedSquash `mapstructure:",squash"`
Vbarfoo string
}

type Map struct {
Vfoo string
Vother map[string]string
Expand Down Expand Up @@ -1051,6 +1105,147 @@ func TestDecode_SquashOnNonStructType(t *testing.T) {
}
}

func TestDecode_SquashOnInterfaceType(t *testing.T) {
t.Parallel()

input := map[string]interface{}{
"VFoo": "42",
"VBar": "43",
}

result := SquashOnInterfaceType{
TestInterface: &TestInterfaceImpl{},
}
err := Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err)
}

res := result.GetVfoo()
if res != "42" {
t.Errorf("unexpected value for VFoo: %s", res)
}

res = result.Vbar
if res != "43" {
t.Errorf("unexpected value for Vbar: %s", res)
}
}

func TestDecode_SquashOnOuterNestedInterfaceType(t *testing.T) {
t.Parallel()

input := map[string]interface{}{
"VFoo": "42",
"VBar": "43",
"Vfoobar": "44",
"Vbarfoo": "45",
}

result := SquashOnNestedInterfaceType{
NestedSquash: NestedSquash{
SquashOnInterfaceType: SquashOnInterfaceType{
TestInterface: &TestInterfaceImpl{},
},
},
}

err := Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err)
}

res := result.NestedSquash.GetVfoo()
if res != "42" {
t.Errorf("unexpected value for VFoo: %s", res)
}

res = result.NestedSquash.Vbar
if res != "43" {
t.Errorf("unexpected value for Vbar: %s", res)
}

res = result.NestedSquash.Vfoobar
if res != "44" {
t.Errorf("unexpected value for Vfoobar: %s", res)
}

res = result.Vbarfoo
if res != "45" {
t.Errorf("unexpected value for Vbarfoo: %s", res)
}
}

func TestDecode_SquashOnInnerNestedInterfaceType(t *testing.T) {
t.Parallel()

input := map[string]interface{}{
"VFoo": "42",
"VBar": "43",
"Vfoobar": "44",
"Vbarfoo": "45",
}

result := SquashOnInterfaceType{
TestInterface: &TestNestedInterfaceImpl{
SquashOnNestedInterfaceType: SquashOnNestedInterfaceType{
NestedSquash: NestedSquash{
SquashOnInterfaceType: SquashOnInterfaceType{
TestInterface: &TestInterfaceImpl{},
},
},
},
},
}

err := Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err)
}

res := result.GetVfoo()
if res != "42" {
t.Errorf("unexpected value for VFoo: %s", res)
}

res = result.Vbar
if res != "43" {
t.Errorf("unexpected value for Vbar: %s", res)
}

res = result.GetVfoobar()
if res != "44" {
t.Errorf("unexpected value for Vfoobar: %s", res)
}

res = result.GetVbarfoo()
if res != "45" {
t.Errorf("unexpected value for Vbarfoo: %s", res)
}
}

func TestDecode_SquashOnNilInterfaceType(t *testing.T) {
t.Parallel()

input := map[string]interface{}{
"VFoo": "42",
"VBar": "43",
}

result := SquashOnInterfaceType{
TestInterface: nil,
}
err := Decode(input, &result)
if err != nil {
t.Fatalf("got an err: %s", err)
}

res := result.Vbar
if res != "43" {
t.Errorf("unexpected value for Vbar: %s", res)
}
}

func TestDecode_DecodeHook(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit c97971d

Please sign in to comment.