This repository has been archived by the owner on Oct 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathoperators.go
220 lines (188 loc) · 6.38 KB
/
operators.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package ldclient
import (
"regexp"
"strings"
"github.com/blang/semver"
)
// List of available operators
const (
OperatorIn Operator = "in"
OperatorEndsWith Operator = "endsWith"
OperatorStartsWith Operator = "startsWith"
OperatorMatches Operator = "matches"
OperatorContains Operator = "contains"
OperatorLessThan Operator = "lessThan"
OperatorLessThanOrEqual Operator = "lessThanOrEqual"
OperatorGreaterThan Operator = "greaterThan"
OperatorGreaterThanOrEqual Operator = "greaterThanOrEqual"
OperatorBefore Operator = "before"
OperatorAfter Operator = "after"
OperatorSegmentMatch Operator = "segmentMatch"
OperatorSemVerEqual Operator = "semVerEqual"
OperatorSemVerLessThan Operator = "semVerLessThan"
OperatorSemVerGreaterThan Operator = "semVerGreaterThan"
)
type opFn (func(interface{}, interface{}) bool)
// Operator describes an operator for a clause
type Operator string
// OpsList is the list of available operators
var OpsList = []Operator{
OperatorIn,
OperatorEndsWith,
OperatorStartsWith,
OperatorMatches,
OperatorContains,
OperatorLessThan,
OperatorLessThanOrEqual,
OperatorGreaterThan,
OperatorGreaterThanOrEqual,
OperatorBefore,
OperatorAfter,
OperatorSegmentMatch,
OperatorSemVerEqual,
OperatorSemVerLessThan,
OperatorSemVerGreaterThan,
}
var versionNumericComponentsRegex = regexp.MustCompile(`^\d+(\.\d+)?(\.\d+)?`)
// Name returns the string name for an operator
func (op Operator) Name() string {
return string(op)
}
var allOps = map[Operator]opFn{
OperatorIn: operatorInFn,
OperatorEndsWith: operatorEndsWithFn,
OperatorStartsWith: operatorStartsWithFn,
OperatorMatches: operatorMatchesFn,
OperatorContains: operatorContainsFn,
OperatorLessThan: operatorLessThanFn,
OperatorLessThanOrEqual: operatorLessThanOrEqualFn,
OperatorGreaterThan: operatorGreaterThanFn,
OperatorGreaterThanOrEqual: operatorGreaterThanOrEqualFn,
OperatorBefore: operatorBeforeFn,
OperatorAfter: operatorAfterFn,
OperatorSemVerEqual: operatorSemVerEqualFn,
OperatorSemVerLessThan: operatorSemVerLessThanFn,
OperatorSemVerGreaterThan: operatorSemVerGreaterThanFn,
}
// Turn this into a static map
func operatorFn(operator Operator) opFn {
if op, ok := allOps[operator]; ok {
return op
}
return operatorNoneFn
}
func operatorInFn(uValue interface{}, cValue interface{}) bool {
if uValue == cValue {
return true
}
if numericOperator(uValue, cValue, func(u float64, c float64) bool { return u == c }) {
return true
}
return false
}
func stringOperator(uValue interface{}, cValue interface{}, fn func(string, string) bool) bool {
if uStr, ok := uValue.(string); ok {
if cStr, ok := cValue.(string); ok {
return fn(uStr, cStr)
}
}
return false
}
func operatorStartsWithFn(uValue interface{}, cValue interface{}) bool {
return stringOperator(uValue, cValue, func(u string, c string) bool { return strings.HasPrefix(u, c) })
}
func operatorEndsWithFn(uValue interface{}, cValue interface{}) bool {
return stringOperator(uValue, cValue, func(u string, c string) bool { return strings.HasSuffix(u, c) })
}
func operatorMatchesFn(uValue interface{}, cValue interface{}) bool {
return stringOperator(uValue, cValue, func(u string, c string) bool {
if matched, err := regexp.MatchString(c, u); err == nil {
return matched
}
return false
})
}
func operatorContainsFn(uValue interface{}, cValue interface{}) bool {
return stringOperator(uValue, cValue, func(u string, c string) bool { return strings.Contains(u, c) })
}
func numericOperator(uValue interface{}, cValue interface{}, fn func(float64, float64) bool) bool {
uFloat64 := ParseFloat64(uValue)
if uFloat64 != nil {
cFloat64 := ParseFloat64(cValue)
if cFloat64 != nil {
return fn(*uFloat64, *cFloat64)
}
}
return false
}
func operatorLessThanFn(uValue interface{}, cValue interface{}) bool {
return numericOperator(uValue, cValue, func(u float64, c float64) bool { return u < c })
}
func operatorLessThanOrEqualFn(uValue interface{}, cValue interface{}) bool {
return numericOperator(uValue, cValue, func(u float64, c float64) bool { return u <= c })
}
func operatorGreaterThanFn(uValue interface{}, cValue interface{}) bool {
return numericOperator(uValue, cValue, func(u float64, c float64) bool { return u > c })
}
func operatorGreaterThanOrEqualFn(uValue interface{}, cValue interface{}) bool {
return numericOperator(uValue, cValue, func(u float64, c float64) bool { return u >= c })
}
func operatorBeforeFn(uValue interface{}, cValue interface{}) bool {
uTime := ParseTime(uValue)
if uTime != nil {
cTime := ParseTime(cValue)
if cTime != nil {
return uTime.Before(*cTime)
}
}
return false
}
func operatorAfterFn(uValue interface{}, cValue interface{}) bool {
uTime := ParseTime(uValue)
if uTime != nil {
cTime := ParseTime(cValue)
if cTime != nil {
return uTime.After(*cTime)
}
}
return false
}
func parseSemVer(value interface{}) (semver.Version, bool) {
if versionStr, ok := value.(string); ok {
if sv, err := semver.Parse(versionStr); err == nil {
return sv, true
}
// Failed to parse as-is; see if we can fix it by adding zeroes
matchParts := versionNumericComponentsRegex.FindStringSubmatch(versionStr)
if matchParts != nil {
transformedVersionStr := matchParts[0]
for i := 1; i < len(matchParts); i++ {
if matchParts[i] == "" {
transformedVersionStr += ".0"
}
}
transformedVersionStr += versionStr[len(matchParts[0]):]
if sv, err := semver.Parse(transformedVersionStr); err == nil {
return sv, true
}
}
}
return semver.Version{}, false
}
func semVerOperator(uValue interface{}, cValue interface{}, fn func(semver.Version, semver.Version) bool) bool {
u, uOk := parseSemVer(uValue)
c, cOk := parseSemVer(cValue)
return uOk && cOk && fn(u, c)
}
func operatorSemVerEqualFn(uValue interface{}, cValue interface{}) bool {
return semVerOperator(uValue, cValue, semver.Version.Equals)
}
func operatorSemVerLessThanFn(uValue interface{}, cValue interface{}) bool {
return semVerOperator(uValue, cValue, semver.Version.LT)
}
func operatorSemVerGreaterThanFn(uValue interface{}, cValue interface{}) bool {
return semVerOperator(uValue, cValue, semver.Version.GT)
}
func operatorNoneFn(uValue interface{}, cValue interface{}) bool {
return false
}