1package copier
2
3import (
4	"database/sql"
5	"errors"
6	"reflect"
7)
8
9// Copy copy things
10func Copy(toValue interface{}, fromValue interface{}) (err error) {
11	var (
12		isSlice bool
13		amount  = 1
14		from    = indirect(reflect.ValueOf(fromValue))
15		to      = indirect(reflect.ValueOf(toValue))
16	)
17
18	if !to.CanAddr() {
19		return errors.New("copy to value is unaddressable")
20	}
21
22	// Return is from value is invalid
23	if !from.IsValid() {
24		return
25	}
26
27	// Just set it if possible to assign
28	if from.Type().AssignableTo(to.Type()) {
29		to.Set(from)
30		return
31	}
32
33	fromType := indirectType(from.Type())
34	toType := indirectType(to.Type())
35
36	if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
37		return
38	}
39
40	if to.Kind() == reflect.Slice {
41		isSlice = true
42		if from.Kind() == reflect.Slice {
43			amount = from.Len()
44		}
45	}
46
47	for i := 0; i < amount; i++ {
48		var dest, source reflect.Value
49
50		if isSlice {
51			// source
52			if from.Kind() == reflect.Slice {
53				source = indirect(from.Index(i))
54			} else {
55				source = indirect(from)
56			}
57
58			// dest
59			dest = indirect(reflect.New(toType).Elem())
60		} else {
61			source = indirect(from)
62			dest = indirect(to)
63		}
64
65		// Copy from field to field or method
66		for _, field := range deepFields(fromType) {
67			name := field.Name
68
69			if fromField := source.FieldByName(name); fromField.IsValid() {
70				// has field
71				if toField := dest.FieldByName(name); toField.IsValid() {
72					if toField.CanSet() {
73						if !set(toField, fromField) {
74							if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
75								return err
76							}
77						}
78					}
79				} else {
80					// try to set to method
81					var toMethod reflect.Value
82					if dest.CanAddr() {
83						toMethod = dest.Addr().MethodByName(name)
84					} else {
85						toMethod = dest.MethodByName(name)
86					}
87
88					if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
89						toMethod.Call([]reflect.Value{fromField})
90					}
91				}
92			}
93		}
94
95		// Copy from method to field
96		for _, field := range deepFields(toType) {
97			name := field.Name
98
99			var fromMethod reflect.Value
100			if source.CanAddr() {
101				fromMethod = source.Addr().MethodByName(name)
102			} else {
103				fromMethod = source.MethodByName(name)
104			}
105
106			if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
107				if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
108					values := fromMethod.Call([]reflect.Value{})
109					if len(values) >= 1 {
110						set(toField, values[0])
111					}
112				}
113			}
114		}
115
116		if isSlice {
117			if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
118				to.Set(reflect.Append(to, dest.Addr()))
119			} else if dest.Type().AssignableTo(to.Type().Elem()) {
120				to.Set(reflect.Append(to, dest))
121			}
122		}
123	}
124	return
125}
126
127func deepFields(reflectType reflect.Type) []reflect.StructField {
128	var fields []reflect.StructField
129
130	if reflectType = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
131		for i := 0; i < reflectType.NumField(); i++ {
132			v := reflectType.Field(i)
133			if v.Anonymous {
134				fields = append(fields, deepFields(v.Type)...)
135			} else {
136				fields = append(fields, v)
137			}
138		}
139	}
140
141	return fields
142}
143
144func indirect(reflectValue reflect.Value) reflect.Value {
145	for reflectValue.Kind() == reflect.Ptr {
146		reflectValue = reflectValue.Elem()
147	}
148	return reflectValue
149}
150
151func indirectType(reflectType reflect.Type) reflect.Type {
152	for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
153		reflectType = reflectType.Elem()
154	}
155	return reflectType
156}
157
158func set(to, from reflect.Value) bool {
159	if from.IsValid() {
160		if to.Kind() == reflect.Ptr {
161			//set `to` to nil if from is nil
162			if from.Kind() == reflect.Ptr && from.IsNil() {
163				to.Set(reflect.Zero(to.Type()))
164				return true
165			} else if to.IsNil() {
166				to.Set(reflect.New(to.Type().Elem()))
167			}
168			to = to.Elem()
169		}
170
171		if from.Type().ConvertibleTo(to.Type()) {
172			to.Set(from.Convert(to.Type()))
173		} else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
174			err := scanner.Scan(from.Interface())
175			if err != nil {
176				return false
177			}
178		} else if from.Kind() == reflect.Ptr {
179			return set(to, from.Elem())
180		} else {
181			return false
182		}
183	}
184	return true
185}
186