-
Notifications
You must be signed in to change notification settings - Fork 1
/
map-matcher.go
143 lines (119 loc) · 2.8 KB
/
map-matcher.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
package extra
import (
"fmt"
"reflect"
"go.uber.org/mock/gomock"
)
// MMatcher interface improved to add functions.
type MMatcher interface {
gomock.Matcher
// Add a matcher for a specific key
Key(key interface{}, match interface{}) MMatcher
}
type mStorage struct {
key interface{}
match interface{}
}
type mapMatcher struct {
keys []*mStorage
}
func (m *mapMatcher) String() string {
str := ""
// Loop over all keys
for in, v := range m.keys {
// Add separator for display
if in > 0 {
str += ", "
}
// Check if key is a gomock matcher
m, ok := v.key.(gomock.Matcher)
if ok {
str += fmt.Sprintf("key %s", m.String())
} else {
str += fmt.Sprintf("key %v", v.key)
}
// Try to cast to a matcher interface
m, ok = v.match.(gomock.Matcher)
// Check if cast is ok
if ok {
str += fmt.Sprintf(" must match %s", m.String())
} else {
str += fmt.Sprintf(" must be equal to %v", v.match)
}
}
return str
}
func (m *mapMatcher) Matches(x interface{}) bool {
// Check if x is nil
if x == nil {
return false
}
// Value of interface input
rval := reflect.ValueOf(x)
rkind := rval.Kind()
// Check if reflect value is supported or not
if rkind != reflect.Map {
return false
}
// Default case
res := len(m.keys) != 0
// Create reflect indirect
// indirect := reflect.Indirect(rval)
// Loop over all matcher keys
for _, kk := range m.keys {
// Store if matcher key can be found
matchKeyFound := false
// Loop over map keys
for _, keyVal := range rval.MapKeys() {
// Get key data
keyD := keyVal.Interface()
// Get reflect value from key
rv := rval.MapIndex(keyVal)
// Get data from key
val := rv.Interface()
// Check if matcher key is matching current key
if !isMatchingData(kk.key, keyD) {
// Skip this key
continue
}
// Set match key as found
matchKeyFound = true
// Check map key value is matching
res = res && isMatchingData(kk.match, val)
// Break the loop at this step
// No need to continue to check map values
break
}
// Check if match key was found
if !matchKeyFound {
// Match key wasn't found, it is an error
return false
}
// Check result
if !res {
// If result isn't true at this step, stop now
return false
}
}
return res
}
func isMatchingData(matchKey, keyData interface{}) bool {
// Check if given key in matcher is a gomock matcher
mk, ok := matchKey.(gomock.Matcher)
if ok {
return mk.Matches(keyData)
}
return matchKey == keyData
}
func (m *mapMatcher) Key(key interface{}, match interface{}) MMatcher {
// Check if key exists
if key == nil {
return m
}
// Key name exists => add data
m.keys = append(m.keys, &mStorage{key: key, match: match})
// Return
return m
}
// MapMatcher will return a new map matcher.
func MapMatcher() MMatcher { return &mapMatcher{} }