-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdelta.go
116 lines (94 loc) · 2.41 KB
/
delta.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
package delta
import (
"errors"
"reflect"
"github.com/fatih/structs"
)
var (
ErrNotStruct = errors.New("The given type is not a struct")
WarnDiffType = errors.New("Warning given types are not of the same type")
)
type fields map[string]interface{}
type structInfo struct {
Fields fields
sInterface interface{}
sValue reflect.Value
sType reflect.Type
}
func getStructInfo(val interface{}) (*structInfo, error) {
if !isStruct(val) {
return nil, ErrNotStruct
}
info := &structInfo{
sInterface: val,
sValue: reflect.ValueOf(val),
sType: reflect.TypeOf(val),
Fields: make(fields),
}
numFields := info.sValue.NumField()
for i := 0; i < numFields; i++ {
field := info.sType.Field(i)
info.Fields[field.Name] = info.sValue.Field(i).Interface()
}
return info, nil
}
func isStruct(val interface{}) bool {
return reflect.ValueOf(val).Kind() == reflect.Struct
}
// Find the delta between 2 structs
func Struct(base, compare interface{}) (map[string]interface{}, error) {
var err error
baseInfo, err := getStructInfo(base)
if err != nil {
return nil, err
}
compareInfo, err := getStructInfo(compare)
if err != nil {
return nil, err
}
diffFields := make(fields)
for fieldName := range compareInfo.Fields {
comparefieldVal := compareInfo.Fields[fieldName]
basefieldVal, _ := baseInfo.Fields[fieldName]
if !equal(comparefieldVal, basefieldVal) {
diffFields[fieldName] = parse(reflect.ValueOf(comparefieldVal))
}
}
return diffFields, nil
}
func parse(val reflect.Value) interface{} {
valKind := val.Kind()
var rtnval interface{}
switch valKind {
case reflect.Struct:
// convert to map
rtnval = structs.Map(val.Interface())
case reflect.Slice, reflect.Array:
// create []interface{}
// iterate over field slice and add each item to the []interface{}
rtnval = parseSlice(val.Interface())
default:
rtnval = val.Interface()
}
return rtnval
}
func parseSlice(slice interface{}) []interface{} {
sliceVal := reflect.ValueOf(slice)
length := sliceVal.Len()
vals := make([]interface{}, 0)
for i := 0; i < length; i++ {
val := sliceVal.Index(i)
vals = append(vals, parse(val))
}
return vals
}
// Returns true if both parameters have the same type and value
// false otherwise
func equal(v1, v2 interface{}) bool {
if reflect.TypeOf(v1).Kind() != reflect.TypeOf(v2).Kind() {
return false
} else if !reflect.DeepEqual(v1, v2) {
return false
}
return true
}