Skip to content
This repository has been archived by the owner on Oct 2, 2020. It is now read-only.

Commit

Permalink
Apply Field Hooks to Embedded struct fields
Browse files Browse the repository at this point in the history
Summary: This PR modifies the FieldHooks to add support for applying
hooks to embedded fields.  We do this on Anonymous (embedded) fields in
the struct.

Test Plan: Wrote test that failed, now it passes.
  • Loading branch information
Will Hughes committed May 19, 2017
1 parent 8b7bb8e commit 80df34a
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 3 deletions.
25 changes: 22 additions & 3 deletions hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func getMapUpdates(destType reflect.Type, srcData reflect.Value, hook FieldHookF
updates := make(map[interface{}]interface{})
var errors []error

decodableFields := getDecodableStructFields(destType)
decodableFields := getDecodableStructFields(destType, tagName)
for _, structField := range decodableFields {
// This field resolution logic is adapted from mapstructure's own
// logic.
Expand Down Expand Up @@ -309,7 +309,7 @@ func getMapUpdates(destType reflect.Type, srcData reflect.Value, hook FieldHookF
return updates, nil
}

func getDecodableStructFields(structType reflect.Type) []reflect.StructField {
func getDecodableStructFields(structType reflect.Type, tagName string) []reflect.StructField {
fields := make([]reflect.StructField, 0)
for i := 0; i < structType.NumField(); i++ {
structField := structType.Field(i)
Expand All @@ -318,7 +318,26 @@ func getDecodableStructFields(structType reflect.Type) []reflect.StructField {
// into it.
continue
}
// TODO account for embedded struct fields

// TODO pull in the squash info from the decoder options
squash := structField.Anonymous
tagParts := strings.Split(structField.Tag.Get(tagName), ",")
for _, tag := range tagParts[1:] {
switch tag {
case "squash":
squash = true
case "nosquash": // explicit opt-out
squash = false
default:
continue
}
break
}

if squash && structField.Type.Kind() == reflect.Struct {
fields = append(fields, getDecodableStructFields(structField.Type, tagName)...)
continue
}
fields = append(fields, structField)
}
return fields
Expand Down
39 changes: 39 additions & 0 deletions hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,45 @@ func TestMultipleFieldHooks(t *testing.T) {
assert.Equal(t, 42, dest.Int)
}

func TestEmbeddedFieldHooks(t *testing.T) {
type subDest struct {
Int int
}
var dest struct {
subDest
}

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

hook1 := newMockFieldHook(mockCtrl)
hook2 := newMockFieldHook(mockCtrl)

typeOfInt := reflect.TypeOf(42)

hook1.
Expect(_typeOfEmptyInterface, structField{
Name: "Int",
Type: typeOfInt,
}, reflectEq{"FOO"}).
Return(valueOf("BAR"), nil)

hook2.
Expect(reflect.TypeOf(""), structField{
Name: "Int",
Type: typeOfInt,
}, reflectEq{"BAR"}).
Return(valueOf(42), nil)

err := Decode(&dest, map[string]interface{}{"int": "FOO"},
FieldHook(hook1.Hook()),
FieldHook(hook2.Hook()),
)
require.NoError(t, err)

assert.Equal(t, 42, dest.Int)
}

func TestMultipleDecodeHooks(t *testing.T) {
type myStruct struct{ String string }

Expand Down

0 comments on commit 80df34a

Please sign in to comment.