Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix converter error do not apply #162

Merged
merged 1 commit into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 45 additions & 22 deletions copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)

for _, k := range from.MapKeys() {
toKey := indirect(reflect.New(toType.Key()))
if !set(toKey, k, opt.DeepCopy, converters) {
isSet, err := set(toKey, k, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
}

Expand All @@ -158,7 +162,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
elemType, _ = indirectType(elemType)
}
toValue := indirect(reflect.New(elemType))
if !set(toValue, from.MapIndex(k), opt.DeepCopy, converters) {
isSet, err = set(toValue, from.MapIndex(k), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
return err
}
Expand Down Expand Up @@ -186,8 +194,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
if to.Len() < i+1 {
to.Set(reflect.Append(to, reflect.New(to.Type().Elem()).Elem()))
}

if !set(to.Index(i), from.Index(i), opt.DeepCopy, converters) {
isSet, err := set(to.Index(i), from.Index(i), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
if err != nil {
Expand Down Expand Up @@ -291,7 +302,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
toField := dest.FieldByName(destFieldName)
if toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField, opt.DeepCopy, converters) {
isSet, err := set(toField, fromField, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
return err
}
Expand Down Expand Up @@ -345,7 +360,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
if to.Len() < i+1 {
to.Set(reflect.Append(to, dest.Addr()))
} else {
if !set(to.Index(i), dest.Addr(), opt.DeepCopy, converters) {
isSet, err := set(to.Index(i), dest.Addr(), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt)
if err != nil {
Expand All @@ -357,7 +376,11 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
if to.Len() < i+1 {
to.Set(reflect.Append(to, dest))
} else {
if !set(to.Index(i), dest, opt.DeepCopy, converters) {
isSet, err := set(to.Index(i), dest, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt)
if err != nil {
Expand Down Expand Up @@ -449,32 +472,32 @@ func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
return reflectType, isPtr
}

func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) bool {
func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) (bool, error) {
if !from.IsValid() {
return true
return true, nil
}
if ok, err := lookupAndCopyWithConverter(to, from, converters); err != nil {
return false
return false, err
} else if ok {
return true
return true, nil
}

if to.Kind() == reflect.Ptr {
// set `to` to nil if from is nil
if from.Kind() == reflect.Ptr && from.IsNil() {
to.Set(reflect.Zero(to.Type()))
return true
return true, nil
} else if to.IsNil() {
// `from` -> `to`
// sql.NullString -> *string
if fromValuer, ok := driverValuer(from); ok {
v, err := fromValuer.Value()
if err != nil {
return false
return true, nil
}
// if `from` is not valid do nothing with `to`
if v == nil {
return true
return true, nil
}
}
// allocate new `to` variable with default value (eg. *string -> new(string))
Expand All @@ -493,10 +516,10 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
}
}
if from.Kind() == reflect.Ptr && from.IsNil() {
return true
return true, nil
}
if toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice {
return false
return false, nil
}
}

Expand All @@ -508,7 +531,7 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
if from.Kind() == reflect.Ptr {
// if `from` is nil do nothing with `to`
if from.IsNil() {
return true
return true, nil
}
// depointer `from`
from = indirect(from)
Expand All @@ -518,18 +541,18 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
// set `to` by invoking method Scan(`from`)
err := toScanner.Scan(from.Interface())
if err != nil {
return false
return false, nil
}
} else if fromValuer, ok := driverValuer(from); ok {
// `from` -> `to`
// sql.NullString -> string
v, err := fromValuer.Value()
if err != nil {
return false
return false, nil
}
// if `from` is not valid do nothing with `to`
if v == nil {
return true
return true, nil
}
rv := reflect.ValueOf(v)
if rv.Type().AssignableTo(to.Type()) {
Expand All @@ -540,10 +563,10 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
} else if from.Kind() == reflect.Ptr {
return set(to, from.Elem(), deepCopy, converters)
} else {
return false
return false, nil
}

return true
return true, nil
}

// lookupAndCopyWithConverter looks up the type pair, on success the TypeConverter Fn func is called to copy src to dst field.
Expand Down
36 changes: 36 additions & 0 deletions copier_converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,39 @@ func TestCopyWithConverterStrToStrPointer(t *testing.T) {
t.Fatalf("got %q, wanted nil", *dst.Field1)
}
}

func TestCopyWithConverterRaisingError(t *testing.T) {
type SrcStruct struct {
Field1 string
}

type DestStruct struct {
Field1 *string
}

src := SrcStruct{
Field1: "",
}

var dst DestStruct

ptrStrType := ""

err := copier.CopyWithOption(&dst, &src, copier.Option{
IgnoreEmpty: false,
DeepCopy: true,
Converters: []copier.TypeConverter{
{
SrcType: copier.String,
DstType: &ptrStrType,
Fn: func(src interface{}) (interface{}, error) {
return nil, errors.New("src type not matching")
},
},
},
})
if err == nil {
t.Fatalf(`Should be raising an error.`)
return
}
}