-
Notifications
You must be signed in to change notification settings - Fork 4
/
mapper.go
96 lines (88 loc) · 2.63 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
package gomapper
import (
"fmt"
"reflect"
)
func validateSource(source any) error {
sourceTypeName := getTypeName(source)
if source == nil {
return fmt.Errorf("source value can't be nil, source type: %s", sourceTypeName)
}
typeOf := reflect.TypeOf(source)
if typeOf.Kind() == reflect.Ptr && typeOf.Elem().Kind() == reflect.Ptr {
return fmt.Errorf("source can have a pointer type, but not a pointer to pointer, source type: %s", sourceTypeName)
}
return nil
}
func prepareSource(source any) any {
sourceToMap := source
sourceValueOf := reflect.ValueOf(source)
if sourceValueOf.Kind() == reflect.Ptr {
sourceToMap = sourceValueOf.Elem().Interface()
}
return sourceToMap
}
func getTypeNameRecursive(target reflect.Type, typeName string) string {
if target.Kind() == reflect.Ptr || target.Kind() == reflect.Slice {
newTarget := target.Elem()
newTypeName := "*" + typeName
if target.Kind() == reflect.Slice {
newTypeName = "[]" + typeName
}
return getTypeNameRecursive(newTarget, newTypeName)
}
return fmt.Sprintf("%s%s.%s", typeName, target.PkgPath(), target.Name())
}
func getTypeName(target any) string {
tt := reflect.TypeOf(target)
if target != nil || tt != nil {
return getTypeNameRecursive(tt, "")
}
return "undefined"
}
func validateDest(dest any) error {
dValueOf := reflect.ValueOf(dest)
dTypeName := getTypeName(dest)
if dValueOf.Kind() != reflect.Ptr {
return fmt.Errorf("destenation value should have a pointer type, but has %s type", dTypeName)
}
if dest == nil || dValueOf.IsNil() {
return fmt.Errorf("destenation value can't be nil, destenation type: %s", dTypeName)
}
if dValueOf.Kind() == reflect.Ptr && dValueOf.Elem().Kind() == reflect.Ptr {
return fmt.Errorf("destenation value should have a pointer type, not a pointer to pointer, but has %s type", dTypeName)
}
return nil
}
// Map source to dest
func Map(source interface{}, dest interface{}) error {
err := validateSource(source)
if err != nil {
return err
}
sourceForMap := prepareSource(source)
err = validateDest(dest)
if err != nil {
return err
}
route, ok := routes[reflect.TypeOf(sourceForMap)]
if !ok {
return fmt.Errorf("route not found for type %s to type %s",
getTypeName(sourceForMap), getTypeName(dest))
}
mapFunc, ok := route[reflect.TypeOf(dest)]
if !ok {
return fmt.Errorf("route not found for type %s to type %s",
getTypeName(sourceForMap), getTypeName(dest))
}
return mapFunc(sourceForMap, dest)
}
// MapTo Map source to the new dest object
func MapTo[TDest interface{}](source interface{}) (TDest, error) {
dest := new(TDest)
err := Map(source, dest)
if err != nil {
return *dest, err
}
return *dest, nil
}