Skip to content

Commit

Permalink
fix: several issues while copy with custom converter
Browse files Browse the repository at this point in the history
As mentioned in issue#170 there're several issues in using custom converter:

1. from struct to struct
2. from map to map
3. from slice to slice
4. from embedded slice to embedded slice

All these cases are tested in `copier_issue170_test.go`. There is
a FIXME leaved behind becaused I'm not very familier with types in golang.
It'll be great if someone could fix it for me.

Resolves: #170
  • Loading branch information
driventokill committed Apr 25, 2023
1 parent 20cee7e commit 780b83e
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 0 deletions.
28 changes: 28 additions & 0 deletions copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
return
}

if len(converters) > 0 {
if ok, e := set(to, from, opt.DeepCopy, converters); e == nil && ok {
// converter supported
return
}
}

if from.Kind() == reflect.Slice || to.Kind() == reflect.Slice {
isSlice = true
if from.Kind() == reflect.Slice {
Expand All @@ -239,6 +246,27 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
dest = indirect(to)
}

if len(converters) > 0 {
if ok, e := set(dest, source, opt.DeepCopy, converters); e == nil && ok {
if isSlice {
// FIXME: maybe should check the other types?
if to.Type().Elem().Kind() == reflect.Ptr {
to.Index(i).Set(dest.Addr())
} else {
if to.Len() < i+1 {
reflect.Append(to, dest)
} else {
to.Index(i).Set(dest)
}
}
} else {
to.Set(dest)
}

continue
}
}

destKind := dest.Kind()
initDest := false
if destKind == reflect.Interface {
Expand Down
112 changes: 112 additions & 0 deletions copier_issue170_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package copier_test

import (
"github.com/jinzhu/copier"
"reflect"
"testing"
)

type A struct {
A int
}
type B struct {
A int
b int
}

var copied = B{A: 2387483274, b: 128387134}

func newOptWithConverter() copier.Option {
return copier.Option{
Converters: []copier.TypeConverter{
{
SrcType: A{},
DstType: B{},
Fn: func(from interface{}) (interface{}, error) {
return copied, nil
},
},
},
}
}

func Test_Struct_With_Converter(t *testing.T) {
aa := A{A: 11}
bb := B{A: 10, b: 100}
err := copier.CopyWithOption(&bb, &aa, newOptWithConverter())
if err != nil || !reflect.DeepEqual(copied, bb) {
t.Fatalf("Got %v, wanted %v", bb, copied)
}
}

func Test_Map_With_Converter(t *testing.T) {
aa := map[string]*A{
"a": &A{A: 10},
}

bb := map[string]*B{
"a": &B{A: 10, b: 100},
}

err := copier.CopyWithOption(&bb, &aa, newOptWithConverter())
if err != nil {
t.Fatalf("copy with converter failed: %v", err)
}

for _, v := range bb {
wanted := &copied
if !reflect.DeepEqual(v, wanted) {
t.Fatalf("Got %v, wanted %v", v, wanted)
}
}
}

func Test_Slice_With_Converter(t *testing.T) {
aa := []*A{
&A{A: 10},
}

bb := []*B{
&B{A: 10, b: 100},
}

err := copier.CopyWithOption(&bb, &aa, newOptWithConverter())

if err != nil {
t.Fatalf("copy slice error: %v", err)
}

wanted := copied
for _, v := range bb {
temp := v
if !reflect.DeepEqual(*temp, wanted) {
t.Fatalf("Got %v, wanted %v", *temp, wanted)
}
}
}

func Test_Slice_Embedded_With_Converter(t *testing.T) {
aa := struct {
A []*A
}{
A: []*A{&A{A: 10}},
}

bb := struct {
A []*B
}{
A: []*B{&B{A: 10, b: 100}},
}

err := copier.CopyWithOption(&bb, &aa, newOptWithConverter())

wanted := struct {
A []*B
}{
A: []*B{&copied},
}

if err != nil || !reflect.DeepEqual(bb, wanted) {
t.Fatalf("Got %v, wanted %v", bb, wanted)
}
}

0 comments on commit 780b83e

Please sign in to comment.