-
Notifications
You must be signed in to change notification settings - Fork 2
/
checker.go
160 lines (143 loc) · 5.97 KB
/
checker.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package structsconv
import (
"fmt"
"log"
"reflect"
)
func checkSetOfRules(set reflect.Value) {
// set must be a struct or a pointer to a struct
if set.Kind() != reflect.Ptr {
log.Panicf("ERROR: Set of Rules must be a pointer to a struct.\n")
}
}
func checkSetDefinitionSupplier(supplier reflect.Value) {
// supplier must be a function
if supplier.Kind() != reflect.Func {
log.Panicf("ERROR: Wrong type of RulesDefinition supplier.\n")
}
// supplier with no arguments
if supplier.Type().NumIn() != 0 {
log.Panicf("ERROR: Wrong number of arguments in RulesDefinition supplier.\n")
}
// supplier with one return value
if supplier.Type().NumOut() != 1 {
log.Panicf("ERROR: Wrong number of return values in RulesDefinition supplier.\n")
}
// supplier return value must be a pointer to a struct
if supplier.Type().Out(0).Kind() != reflect.Struct && supplier.Type().Out(0).Kind() != reflect.Ptr {
log.Panicf("ERROR: Wrong type of return value in RulesDefinition supplier. "+
"Expected 'RulesDefinition' but got '%s'.\n", supplier.Type().Out(0).Kind().String(),
)
}
// if supplier return value is a pointer, it must be a pointer to a struct
if supplier.Type().Out(0).Kind() == reflect.Ptr && supplier.Type().Out(0).Elem().Kind() != reflect.Struct {
log.Panicf("ERROR: Wrong type of return value in RulesDefinition supplier. "+
"Expected 'RulesDefinition' but got '%s'.\n", supplier.Type().Out(0).Kind().String(),
)
}
// if supplier return value is a struct, it must be a RulesDefinition struct
if supplier.Type().Out(0).Kind() == reflect.Struct && supplier.Type().Out(0).Name() != "RulesDefinition" {
log.Panicf("ERROR: Wrong type of return value in RulesDefinition supplier. "+
"Expected 'RulesDefinition' but got '%s'.\n", supplier.Type().Out(0).Name(),
)
}
// if supplier return value is a pointer, it must be a pointer to a RulesDefinition struct
if supplier.Type().Out(0).Kind() == reflect.Ptr && supplier.Type().Out(0).Elem().Name() != "RulesDefinition" {
log.Panicf("ERROR: Wrong type of return value in RulesDefinition supplier. "+
"Expected 'RulesDefinition' but got '%s'.\n", supplier.Type().Out(0).Elem().Name(),
)
}
}
// checkRootValuesTypes checks if the ROOT source and target types are valid.
func checkRootValuesTypes(st, tt reflect.Value) error {
if st.Kind() != reflect.Ptr {
return fmt.Errorf("rules error: source must be a pointer")
}
if tt.Kind() != reflect.Ptr {
return fmt.Errorf("rules error: target must be a pointer")
}
if st.Elem().Kind() != reflect.Struct {
return fmt.Errorf("rules error: source must be a pointer to a struct")
}
if tt.Elem().Kind() != reflect.Struct {
return fmt.Errorf("rules error: target must be a pointer to a struct")
}
return nil
}
// checkMapperRules checks if the mapper rules are valid
func checkMapperRules(key rulesKey, rules RulesSet) {
log.Printf("Checking rules for mapping (%s -> %s).\n", key.source.String(), key.target.String())
for k, r := range rules {
if r == nil { // nil rule == ignore field
log.Printf(
"INFO: (%s -> %s) Field '%s' is marked as ignored.\n",
key.source.String(), key.target.String(), k,
)
continue
}
checkTargetKeyName(k, key)
t := reflect.TypeOf(r)
switch t.Kind() {
case reflect.String: // mapping source field name
checkMappingName(r.(string), k, key)
case reflect.Func: // mapping target field value from function
checkFunc(t, k, key)
default: // not valid rule
log.Panicf(
"ERROR: (%s -> %s) Rule '%s' is not valid. Rule = %v.\n",
key.source.String(), key.target.String(), k, r,
)
}
}
}
// checkTargetKeyName checks if field name (ruleKey) is present in target struct
func checkTargetKeyName(ruleKeyValue string, key rulesKey) {
_, exist := key.target.FieldByName(ruleKeyValue)
if !exist {
log.Panicf(
"ERROR: (%s -> %s) Field '%s' is not present in target struct %s.\n",
key.source.String(), key.target.String(), ruleKeyValue, key.target.String(),
)
}
}
// checkMappingName checks field MappingName in source struct
// - MappingName is present in source struct
// - field kind is the same in origin and target struct
func checkMappingName(mappingName, ruleKey string, key rulesKey) {
sf, exist := key.source.FieldByName(mappingName)
if !exist { // checks if MappingName is present in origin struct
log.Panicf(
"ERROR: (%s -> %s) Field '%s' is not present in source struct %s.\n",
key.source.String(), key.target.String(), mappingName, key.source.String(),
)
}
tf, _ := key.target.FieldByName(ruleKey)
switch {
case sf.Type.Kind() == reflect.Ptr && tf.Type.Kind() == reflect.Ptr && sf.Type.Elem().Kind() == tf.Type.Elem().Kind(), // both are pointers to the same type
sf.Type.Kind() == reflect.Ptr && tf.Type.Kind() != reflect.Ptr && sf.Type.Elem().Kind() == tf.Type.Kind(), // source is pointer to the same type as target
sf.Type.Kind() != reflect.Ptr && tf.Type.Kind() == reflect.Ptr && sf.Type.Kind() == tf.Type.Elem().Kind(), // target is pointer to the same type as source
sf.Type.Kind() == tf.Type.Kind(): // both are the same type
return
default:
log.Panicf(
"ERROR: (%s -> %s) Field '%s' has different type in source (%s:%s) and target (%s:%s) structs.\n",
key.source.String(), key.target.String(), ruleKey, mappingName, sf.Type.String(), ruleKey, tf.Type.String(),
)
}
}
// checkFunc checks if function is valid according to the following criteria:
// - the function returns a value of the same type as the target
func checkFunc(f reflect.Type, ruleKey string, key rulesKey) {
// checks if the function returns a value of the same type as the target
if getFieldByName(ruleKey, key.target).Type != f.Out(0) {
log.Panicf(
"ERROR: (%s -> %s) Function '%s' must return type '%s', currently returns '%s'. Function = '%s'.\n",
key.source.String(), key.target.String(), ruleKey, getFieldByName(ruleKey, key.target).Type.String(), f.Out(0).String(), f.String(),
)
}
}
// getFieldByName returns field by name
func getFieldByName(n string, t reflect.Type) reflect.StructField {
f, _ := t.FieldByName(n)
return f
}