-
Notifications
You must be signed in to change notification settings - Fork 0
/
mapper.go
109 lines (92 loc) · 2.94 KB
/
mapper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package conversion
import (
"reflect"
)
// Mapper maps values from one type to another
type Mapper interface {
// Map maps the value to the destination. The destination must be a pointer to a value.
Map(value, destination any) error
// MapTo maps the value to a new instance of the destination type
MapTo(value any, destinationType reflect.Type) (any, error)
}
type mapper struct {
typeConverters []TypeConverter
}
// Map maps the value to the destination. The destination must be a pointer to a value.
func (m *mapper) Map(source, destination any) error {
sourceValue := reflect.ValueOf(source)
destinationValue := reflect.ValueOf(destination)
if destinationValue.Kind() != reflect.Ptr {
return NewMappingError(source, sourceValue.Type(), destinationValue.Type())
}
destinationValue = destinationValue.Elem()
if !sourceValue.IsValid() {
destinationValue.Set(reflect.Zero(destinationValue.Type()))
return nil
}
sourceType := sourceValue.Type()
destinationType := destinationValue.Type()
if sourceType == destinationType {
destinationValue.Set(sourceValue)
return nil
}
if sourceType.Kind() == reflect.Ptr && sourceType.Elem() == destinationType {
destinationValue.Set(sourceValue.Elem())
return nil
}
if sourceType.Kind() == reflect.Ptr && destinationType.Kind() == reflect.Ptr && sourceType.Elem() == destinationType.Elem() {
destinationValue.Set(sourceValue)
return nil
}
if sourceType.Kind() != reflect.Ptr && destinationType.Kind() == reflect.Ptr && sourceType == destinationType.Elem() {
destinationValue.Set(reflect.New(sourceType))
destinationValue.Elem().Set(sourceValue)
return nil
}
st := sourceType
dt := destinationType
if sourceType.Kind() == reflect.Ptr {
st = sourceType.Elem()
}
if destinationType.Kind() == reflect.Ptr {
dt = destinationType.Elem()
}
sv := sourceValue
dv := destinationValue
if sv.Kind() == reflect.Ptr {
sv = sv.Elem()
}
// if dv is not nil, and it's a pointer, set dv to the pointer's value
if dv.IsValid() && dv.Kind() == reflect.Ptr {
dv = dv.Elem()
}
for _, converter := range m.typeConverters {
if converter.CanConvert(st, dt) {
value, err := converter.Convert(sv, dv, st, dt, m)
if err != nil {
return err
}
if destinationType.Kind() == reflect.Ptr {
destinationValue.Set(reflect.New(destinationType.Elem()))
destinationValue.Elem().Set(value)
} else {
destinationValue.Set(value)
}
return nil
}
}
return NewMappingError(source, sourceType, destinationType)
}
// MapTo maps the value to a new instance of the destination type
func (m *mapper) MapTo(value any, destinationType reflect.Type) (any, error) {
// if value is nil, return zero value of destination type
if value == nil {
return reflect.Zero(destinationType).Interface(), nil
}
destinationValue := reflect.New(destinationType).Elem()
err := m.Map(value, destinationValue.Addr().Interface())
if err != nil {
return nil, err
}
return destinationValue.Interface(), nil
}