diff --git a/copier.go b/copier.go index 0d21d14..b797156 100644 --- a/copier.go +++ b/copier.go @@ -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 { @@ -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 { diff --git a/copier_issue170_test.go b/copier_issue170_test.go new file mode 100644 index 0000000..f97278b --- /dev/null +++ b/copier_issue170_test.go @@ -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) + } +}