Skip to content

Commit

Permalink
chore: refactor mapping unmarshaler (#4145)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevwan committed May 12, 2024
1 parent f10084a commit 0cac41a
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 28 deletions.
64 changes: 37 additions & 27 deletions core/mapping/unmarshaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,6 @@ func (u *Unmarshaler) unmarshalValuer(m Valuer, v any, fullName string) error {
return u.unmarshalWithFullName(simpleValuer{current: m}, v, fullName)
}

func (u *Unmarshaler) fillJsonUnmarshalerStruct(fieldType reflect.Type,
value reflect.Value, targetValue string) error {
if !value.CanSet() {
return errValueNotSettable
}

baseType := Deref(fieldType)
target := reflect.New(baseType)
unmarshaler, ok := target.Interface().(json.Unmarshaler)
if !ok {
return errUnsupportedType
}

if err := unmarshaler.UnmarshalJSON([]byte(targetValue)); err != nil {
return err
}

value.Set(target)
return nil
}

func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value,
mapValue any, fullName string) error {
if !value.CanSet() {
Expand Down Expand Up @@ -330,6 +309,32 @@ func (u *Unmarshaler) fillSliceWithDefault(derefedType reflect.Type, value refle
return u.fillSlice(derefedType, value, slice, fullName)
}

func (u *Unmarshaler) fillUnmarshalerStruct(fieldType reflect.Type,
value reflect.Value, targetValue string) error {
if !value.CanSet() {
return errValueNotSettable
}

baseType := Deref(fieldType)
target := reflect.New(baseType)
switch u.key {
case jsonTagKey:
unmarshaler, ok := target.Interface().(json.Unmarshaler)
if !ok {
return errUnsupportedType
}

if err := unmarshaler.UnmarshalJSON([]byte(targetValue)); err != nil {
return err
}
default:
return errUnsupportedType
}

value.Set(target)
return nil
}

func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue any,
fullName string) (reflect.Value, error) {
mapType := reflect.MapOf(keyType, elemType)
Expand Down Expand Up @@ -423,6 +428,15 @@ func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue any,
return targetValue, nil
}

func (u *Unmarshaler) implementsUnmarshaler(t reflect.Type) bool {
switch u.key {
case jsonTagKey:
return t.Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem())
default:
return false
}
}

func (u *Unmarshaler) parseOptionsWithContext(field reflect.StructField, m Valuer, fullName string) (
string, *fieldOptionsWithContext, error) {
key, options, err := parseKeyAndOptions(u.key, field)
Expand Down Expand Up @@ -600,8 +614,8 @@ func (u *Unmarshaler) processFieldNotFromString(fieldType reflect.Type, value re
return u.fillSliceFromString(fieldType, value, mapValue, fullName)
case valueKind == reflect.String && derefedFieldType == durationType:
return fillDurationValue(fieldType, value, mapValue.(string))
case valueKind == reflect.String && typeKind == reflect.Struct && implementsJsonUnmarshaler(fieldType):
return u.fillJsonUnmarshalerStruct(fieldType, value, mapValue.(string))
case valueKind == reflect.String && typeKind == reflect.Struct && u.implementsUnmarshaler(fieldType):
return u.fillUnmarshalerStruct(fieldType, value, mapValue.(string))
default:
return u.processFieldPrimitive(fieldType, value, mapValue, opts, fullName)
}
Expand Down Expand Up @@ -1087,10 +1101,6 @@ func getValueWithChainedKeys(m valuerWithParent, keys []string) (any, bool) {
}
}

func implementsJsonUnmarshaler(t reflect.Type) bool {
return t.Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem())
}

func join(elem ...string) string {
var builder strings.Builder

Expand Down
26 changes: 25 additions & 1 deletion core/mapping/unmarshaler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5761,7 +5761,7 @@ func TestUnmarshalWithIgnoreFields(t *testing.T) {
}
}

func TestUnmarshal_JsonUnmarshaler(t *testing.T) {
func TestUnmarshal_Unmarshaler(t *testing.T) {
t.Run("success", func(t *testing.T) {
v := struct {
Foo *mockUnmarshaler `json:"name"`
Expand All @@ -5778,6 +5778,30 @@ func TestUnmarshal_JsonUnmarshaler(t *testing.T) {
body := `{"name": "hello"}`
assert.Error(t, UnmarshalJsonBytes([]byte(body), &v))
})

t.Run("not json unmarshaler", func(t *testing.T) {
v := struct {
Foo *struct {
Name string
} `key:"name"`
}{}
u := NewUnmarshaler(defaultKeyName)
assert.Error(t, u.Unmarshal(map[string]any{
"name": "hello",
}, &v))
})

t.Run("not with json key", func(t *testing.T) {
v := struct {
Foo *mockUnmarshaler `json:"name"`
}{}
u := NewUnmarshaler(defaultKeyName)
// with different key, ignore
assert.NoError(t, u.Unmarshal(map[string]any{
"name": "hello",
}, &v))
assert.Nil(t, v.Foo)
})
}

func BenchmarkDefaultValue(b *testing.B) {
Expand Down

0 comments on commit 0cac41a

Please sign in to comment.