1 package evaluation
2
3 import (
4 "strings"
5 "time"
6
7 "github.com/launchdarkly/go-sdk-common/v3/ldattr"
8 "github.com/launchdarkly/go-sdk-common/v3/ldcontext"
9 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
10 "github.com/launchdarkly/go-server-sdk-evaluation/v2/ldmodel"
11 )
12
13 func (es *evaluationScope) clauseMatchesContext(clause *ldmodel.Clause, stack evaluationStack) (bool, error) {
14
15
16
17 if clause.Op == ldmodel.OperatorSegmentMatch {
18 for _, value := range clause.Values {
19 if value.Type() == ldvalue.StringType {
20 if segment := es.owner.dataProvider.GetSegment(value.StringValue()); segment != nil {
21 match, err := es.segmentContainsContext(segment, stack)
22 if err != nil {
23 return false, err
24 }
25 if match {
26 return !clause.Negate, nil
27 }
28 }
29 }
30 }
31 return clause.Negate, nil
32 }
33
34 return clauseMatchesContextNoSegments(clause, &es.context)
35 }
36
37 func clauseMatchesContextNoSegments(c *ldmodel.Clause, context *ldcontext.Context) (bool, error) {
38 if !c.Attribute.IsDefined() {
39 return false, emptyAttrRefError{}
40 }
41 if c.Attribute.Err() != nil {
42 return false, badAttrRefError(c.Attribute.String())
43 }
44 if c.Attribute.String() == ldattr.KindAttr {
45 return maybeNegate(c.Negate, clauseMatchByKind(c, context)), nil
46 }
47 actualContext := context.IndividualContextByKind(c.ContextKind)
48 if !actualContext.IsDefined() {
49 return false, nil
50 }
51 uValue := actualContext.GetValueForRef(c.Attribute)
52 if uValue.IsNull() {
53
54 return false, nil
55 }
56
57
58 if uValue.Type() == ldvalue.ArrayType {
59 for i := 0; i < uValue.Count(); i++ {
60 if matchAny(c, uValue.GetByIndex(i)) {
61 return maybeNegate(c.Negate, true), nil
62 }
63 }
64 return maybeNegate(c.Negate, false), nil
65 }
66
67 return maybeNegate(c.Negate, matchAny(c, uValue)), nil
68 }
69
70 func maybeNegate(negate, result bool) bool {
71 if negate {
72 return !result
73 }
74 return result
75 }
76
77 func matchAny(
78 c *ldmodel.Clause,
79 value ldvalue.Value,
80 ) bool {
81 if c.Op == ldmodel.OperatorIn {
82 return ldmodel.EvaluatorAccessors.ClauseFindValue(c, value)
83 }
84 for i, v := range c.Values {
85 if doOp(c, value, v, i) {
86 return true
87 }
88 }
89 return false
90 }
91
92 func doOp(c *ldmodel.Clause, ctxValue, clValue ldvalue.Value, index int) bool {
93 switch c.Op {
94 case ldmodel.OperatorEndsWith:
95 return stringOperator(ctxValue, clValue, strings.HasSuffix)
96 case ldmodel.OperatorStartsWith:
97 return stringOperator(ctxValue, clValue, strings.HasPrefix)
98 case ldmodel.OperatorMatches:
99 return operatorMatchesFn(c, ctxValue, index)
100 case ldmodel.OperatorContains:
101 return stringOperator(ctxValue, clValue, strings.Contains)
102 case ldmodel.OperatorLessThan:
103 return numericOperator(ctxValue, clValue, func(a float64, b float64) bool { return a < b })
104 case ldmodel.OperatorLessThanOrEqual:
105 return numericOperator(ctxValue, clValue, func(a float64, b float64) bool { return a <= b })
106 case ldmodel.OperatorGreaterThan:
107 return numericOperator(ctxValue, clValue, func(a float64, b float64) bool { return a > b })
108 case ldmodel.OperatorGreaterThanOrEqual:
109 return numericOperator(ctxValue, clValue, func(a float64, b float64) bool { return a >= b })
110 case ldmodel.OperatorBefore:
111 return dateOperator(c, ctxValue, index, time.Time.Before)
112 case ldmodel.OperatorAfter:
113 return dateOperator(c, ctxValue, index, time.Time.After)
114 case ldmodel.OperatorSemVerEqual:
115 return semVerOperator(c, ctxValue, index, 0)
116 case ldmodel.OperatorSemVerLessThan:
117 return semVerOperator(c, ctxValue, index, -1)
118 case ldmodel.OperatorSemVerGreaterThan:
119 return semVerOperator(c, ctxValue, index, 1)
120 }
121 return false
122 }
123
124 func clauseMatchByKind(c *ldmodel.Clause, context *ldcontext.Context) bool {
125
126
127
128 if context.Multiple() {
129 for i := 0; i < context.IndividualContextCount(); i++ {
130 if individualContext := context.IndividualContextByIndex(i); individualContext.IsDefined() {
131 ctxValue := ldvalue.String(string(individualContext.Kind()))
132 if matchAny(c, ctxValue) {
133 return true
134 }
135 }
136 }
137 return false
138 }
139 ctxValue := ldvalue.String(string(context.Kind()))
140 return matchAny(c, ctxValue)
141 }
142
143 func stringOperator(
144 ctxValue, clValue ldvalue.Value,
145 stringTestFn func(string, string) bool,
146 ) bool {
147 if ctxValue.IsString() && clValue.IsString() {
148 return stringTestFn(ctxValue.StringValue(), clValue.StringValue())
149 }
150 return false
151 }
152
153 func operatorMatchesFn(c *ldmodel.Clause, ctxValue ldvalue.Value, clValueIndex int) bool {
154 if ctxValue.IsString() {
155 r := ldmodel.EvaluatorAccessors.ClauseGetValueAsRegexp(c, clValueIndex)
156 if r != nil {
157 return r.MatchString(ctxValue.StringValue())
158 }
159 }
160 return false
161 }
162
163 func numericOperator(ctxValue, clValue ldvalue.Value, fn func(float64, float64) bool) bool {
164 if ctxValue.IsNumber() && clValue.IsNumber() {
165 return fn(ctxValue.Float64Value(), clValue.Float64Value())
166 }
167 return false
168 }
169
170 func dateOperator(
171 c *ldmodel.Clause,
172 ctxValue ldvalue.Value,
173 clValueIndex int,
174 fn func(time.Time, time.Time) bool,
175 ) bool {
176 if clValueTime, ok := ldmodel.EvaluatorAccessors.ClauseGetValueAsTimestamp(c, clValueIndex); ok {
177 if ctxValueTime, ok := ldmodel.TypeConversions.ValueToTimestamp(ctxValue); ok {
178 return fn(ctxValueTime, clValueTime)
179 }
180 }
181 return false
182 }
183
184 func semVerOperator(
185 c *ldmodel.Clause,
186 ctxValue ldvalue.Value,
187 clValueIndex int,
188 expectedComparisonResult int,
189 ) bool {
190 if clValueVer, ok := ldmodel.EvaluatorAccessors.ClauseGetValueAsSemanticVersion(c, clValueIndex); ok {
191 if ctxValueVer, ok := ldmodel.TypeConversions.ValueToSemanticVersion(ctxValue); ok {
192 return ctxValueVer.ComparePrecedence(clValueVer) == expectedComparisonResult
193 }
194 }
195 return false
196 }
197
View as plain text