Skip to content

Commit 62cc012

Browse files
committedJul 19, 2019
Add checking convertable types.
1 parent 8066ba3 commit 62cc012

File tree

2 files changed

+103
-45
lines changed

2 files changed

+103
-45
lines changed
 

‎magic.go

+16-18
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ func isPtrOf(v1, v2 reflect.Value) bool {
1414
return false
1515
}
1616

17-
return v1.Type().Elem() == v2.Type()
18-
}
19-
func isPtrOfStruct(v reflect.Value) bool {
20-
return v.Type().Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct
17+
return v1.Type().Elem().Kind() == v2.Type().Kind()
2118
}
2219

2320
func convert(from, to reflect.Value, opts *options) error {
@@ -28,6 +25,12 @@ func convert(from, to reflect.Value, opts *options) error {
2825
return nil
2926
}
3027

28+
// Convertable
29+
if from.Type().Kind() == to.Type().Kind() && from.Type().ConvertibleTo(to.Type()) {
30+
to.Set(from.Convert(to.Type()))
31+
return nil
32+
}
33+
3134
// Type to Ptr
3235
if isPtrOf(to, from) {
3336
if to.IsNil() {
@@ -49,20 +52,6 @@ func convert(from, to reflect.Value, opts *options) error {
4952
return convertStruct(from, to, opts)
5053
}
5154

52-
if from.Type().Kind() == reflect.Struct && isPtrOfStruct(to) {
53-
if to.IsNil() {
54-
to.Set(reflect.New(to.Type().Elem()))
55-
}
56-
return convertStruct(from, to.Elem(), opts)
57-
}
58-
59-
if to.Type().Kind() == reflect.Struct && isPtrOfStruct(from) {
60-
if from.IsNil() {
61-
from.Set(reflect.New(from.Type()).Elem())
62-
}
63-
return convertStruct(from.Elem(), to, opts)
64-
}
65-
6655
// Slices
6756
if from.Type().Kind() == reflect.Slice && to.Type().Kind() == reflect.Slice {
6857
return convertSlice(from, to, opts)
@@ -129,11 +118,20 @@ func Map(from, to interface{}, opts ...func(*options)) error {
129118
typeTo := reflect.TypeOf(to)
130119
// fmt.Println("Map", typeFrom, "(", valueFrom, ")", "->", typeTo, "(", valueTo, ")")
131120

121+
if typeFrom.Kind() == reflect.Ptr {
122+
valueFrom = valueFrom.Elem()
123+
typeFrom = typeFrom.Elem()
124+
}
125+
132126
if typeTo.Kind() == reflect.Ptr {
133127
valueTo = valueTo.Elem()
134128
typeTo = typeTo.Elem()
135129
}
136130

131+
if !valueTo.CanAddr() {
132+
return fmt.Errorf("%T is not addressable", to)
133+
}
134+
137135
o := options{}
138136
for _, option := range opts {
139137
option(&o)

‎magic_test.go

+87-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package magic
22

33
import (
4+
"fmt"
45
"reflect"
56
"testing"
67
"time"
@@ -10,6 +11,13 @@ func val(i interface{}) reflect.Value {
1011
return reflect.ValueOf(i)
1112
}
1213

14+
func assert(t *testing.T, i1, i2 interface{}) {
15+
t.Helper()
16+
if i1 != i2 {
17+
t.Fatalf("%v != %v", i1, i2)
18+
}
19+
}
20+
1321
type testType1 struct {
1422
ID int
1523
Name string
@@ -44,12 +52,17 @@ type testType6 struct {
4452
Created int64
4553
}
4654

47-
type testType7 struct {
48-
T *testType1
49-
}
55+
func timeToUnix(from, to reflect.Value) (bool, error) {
56+
if to.Type() != reflect.TypeOf(int64(0)) {
57+
return false, nil
58+
}
59+
t, ok := from.Interface().(time.Time)
60+
if ok {
61+
to.SetInt(t.Unix())
62+
return true, nil
63+
}
5064

51-
type testType8 struct {
52-
T *testType2
65+
return false, nil
5366
}
5467

5568
func TestMapStruct(t *testing.T) {
@@ -60,6 +73,12 @@ func TestMapStruct(t *testing.T) {
6073
assert(t, err, nil)
6174
assert(t, t2.ID, t1.ID)
6275
assert(t, t2.Name, t1.Name)
76+
77+
t2 = testType2{}
78+
err = Map(&t1, &t2)
79+
assert(t, err, nil)
80+
assert(t, t2.ID, t1.ID)
81+
assert(t, t2.Name, t1.Name)
6382
}
6483

6584
func TestMapStructWithPointers(t *testing.T) {
@@ -130,19 +149,6 @@ func TestMapPointersSlice(t *testing.T) {
130149
assert(t, t1[0].Tags[0], t2[0].Tags[0])
131150
}
132151

133-
func timeToUnix(from, to reflect.Value) (bool, error) {
134-
if to.Type() != reflect.TypeOf(int64(0)) {
135-
return false, nil
136-
}
137-
t, ok := from.Interface().(time.Time)
138-
if ok {
139-
to.SetInt(t.Unix())
140-
return true, nil
141-
}
142-
143-
return false, nil
144-
}
145-
146152
func TestInvalidType(t *testing.T) {
147153
s1 := struct {
148154
ID int
@@ -171,21 +177,37 @@ func TestInvalidSlice(t *testing.T) {
171177

172178
func TestPtrToType(t *testing.T) {
173179
i := 4385
180+
type Foo1 struct {
181+
Bar int
182+
}
183+
type Foo2 struct {
184+
Bar int
185+
S float64
186+
}
187+
188+
f := Foo1{56}
174189
s1 := struct {
175-
ID *int
176-
}{&i}
190+
ID *int
191+
Foo *Foo1
192+
}{&i, &f}
177193
s2 := struct {
178-
ID int
194+
ID int
195+
Foo Foo2
179196
}{}
180197

181198
err := Map(s1, &s2)
182199
assert(t, err, nil)
183200
assert(t, *s1.ID, s2.ID)
201+
assert(t, s1.Foo.Bar, s2.Foo.Bar)
184202

185203
s1.ID = nil
204+
s1.Foo = nil
186205
err = Map(s1, &s2)
187206
assert(t, err, nil)
188207
assert(t, s2.ID, i)
208+
if s1.Foo != nil {
209+
t.Fatal(s1.Foo)
210+
}
189211
}
190212

191213
func TestTypeToPtr(t *testing.T) {
@@ -201,6 +223,44 @@ func TestTypeToPtr(t *testing.T) {
201223
assert(t, s1.ID, *s2.ID)
202224
}
203225

226+
func TestConvertable(t *testing.T) {
227+
type Maps map[string]string
228+
type S1 struct {
229+
V map[string]string
230+
}
231+
type S2 struct {
232+
V Maps
233+
}
234+
235+
s1 := S1{map[string]string{
236+
"foo": "bar",
237+
}}
238+
s2 := S2{}
239+
240+
err := Map(s1, &s2)
241+
assert(t, err, nil)
242+
assert(t, len(s1.V), len(s2.V))
243+
assert(t, s1.V["foo"], s2.V["foo"])
244+
}
245+
246+
func TestMapError(t *testing.T) {
247+
type S1 struct {
248+
ID int
249+
}
250+
s1 := S1{45}
251+
s2 := []string{}
252+
253+
err := Map(s1, s2)
254+
if err == nil || err.Error() != "[]string is not addressable" {
255+
t.Fatal(err)
256+
}
257+
258+
err = Map(s1, &s2)
259+
if err == nil || err.Error() != "Cannot map magic.S1 to *[]string" {
260+
t.Fatal(err)
261+
}
262+
}
263+
204264
func TestConverter(t *testing.T) {
205265
now := time.Now()
206266
t5 := testType5{45, now}
@@ -210,6 +270,12 @@ func TestConverter(t *testing.T) {
210270
assert(t, err, nil)
211271
assert(t, t5.ID, t6.ID)
212272
assert(t, t6.Created, now.Unix())
273+
274+
e := fmt.Errorf("E")
275+
err = Map(t5, &t6, WithConverters(func(v1, v2 reflect.Value) (bool, error) {
276+
return false, e
277+
}))
278+
assert(t, err.Error(), "Created: E")
213279
}
214280

215281
func TestMapping(t *testing.T) {
@@ -226,9 +292,3 @@ func TestMapping(t *testing.T) {
226292
assert(t, err, nil)
227293
assert(t, t1.ID, t2.UUID)
228294
}
229-
230-
func assert(t *testing.T, i1, i2 interface{}) {
231-
if i1 != i2 {
232-
t.Fatalf("%v != %v", i1, i2)
233-
}
234-
}

0 commit comments

Comments
 (0)
Please sign in to comment.