...
1 package ldmodel
2
3 import (
4 "regexp"
5 "time"
6
7 "github.com/launchdarkly/go-semver"
8
9 "github.com/launchdarkly/go-sdk-common/v3/ldvalue"
10 )
11
12 type targetPreprocessedData struct {
13 valuesMap map[string]struct{}
14 }
15
16 type segmentPreprocessedData struct {
17 includeMap map[string]struct{}
18 excludeMap map[string]struct{}
19 }
20
21 type clausePreprocessedData struct {
22 values []clausePreprocessedValue
23 valuesMap map[jsonPrimitiveValueKey]struct{}
24 }
25
26 type clausePreprocessedValue struct {
27 computed bool
28 valid bool
29 parsedRegexp *regexp.Regexp
30 parsedTime time.Time
31 parsedSemver semver.Version
32 }
33
34 type jsonPrimitiveValueKey struct {
35 valueType ldvalue.ValueType
36 booleanValue bool
37 numberValue float64
38 stringValue string
39 }
40
41 func (j jsonPrimitiveValueKey) isValid() bool {
42 return j.valueType != ldvalue.NullType
43 }
44
45
46
47
48
49
50
51 func PreprocessFlag(f *FeatureFlag) {
52 for i, t := range f.Targets {
53 f.Targets[i].preprocessed.valuesMap = preprocessStringSet(t.Values)
54 }
55 for i, r := range f.Rules {
56 for j, c := range r.Clauses {
57 f.Rules[i].Clauses[j].preprocessed = preprocessClause(c)
58 }
59 }
60 }
61
62
63
64
65
66
67
68 func PreprocessSegment(s *Segment) {
69 p := segmentPreprocessedData{}
70 p.includeMap = preprocessStringSet(s.Included)
71 p.excludeMap = preprocessStringSet(s.Excluded)
72 for i, t := range s.IncludedContexts {
73 s.IncludedContexts[i].preprocessed.valuesMap = preprocessStringSet(t.Values)
74 }
75 for i, t := range s.ExcludedContexts {
76 s.ExcludedContexts[i].preprocessed.valuesMap = preprocessStringSet(t.Values)
77 }
78 s.preprocessed = p
79
80 for i, r := range s.Rules {
81 for j, c := range r.Clauses {
82 s.Rules[i].Clauses[j].preprocessed = preprocessClause(c)
83 }
84 }
85 }
86
87 func preprocessClause(c Clause) clausePreprocessedData {
88 ret := clausePreprocessedData{}
89 switch c.Op {
90 case OperatorIn:
91
92
93
94
95 if len(c.Values) > 1 {
96 valid := true
97 m := make(map[jsonPrimitiveValueKey]struct{}, len(c.Values))
98 for _, v := range c.Values {
99 if key := asPrimitiveValueKey(v); key.isValid() {
100 m[key] = struct{}{}
101 } else {
102 valid = false
103 break
104 }
105 }
106 if valid {
107 ret.valuesMap = m
108 }
109 }
110 case OperatorMatches:
111 ret.values = preprocessValues(c.Values, func(v ldvalue.Value) clausePreprocessedValue {
112 r := parseRegexp(v)
113 return clausePreprocessedValue{valid: r != nil, parsedRegexp: r}
114 })
115 case OperatorBefore, OperatorAfter:
116 ret.values = preprocessValues(c.Values, func(v ldvalue.Value) clausePreprocessedValue {
117 t, ok := parseDateTime(v)
118 return clausePreprocessedValue{valid: ok, parsedTime: t}
119 })
120 case OperatorSemVerEqual, OperatorSemVerGreaterThan, OperatorSemVerLessThan:
121 ret.values = preprocessValues(c.Values, func(v ldvalue.Value) clausePreprocessedValue {
122 s, ok := parseSemVer(v)
123 return clausePreprocessedValue{valid: ok, parsedSemver: s}
124 })
125 default:
126 }
127 return ret
128 }
129
130 func asPrimitiveValueKey(v ldvalue.Value) jsonPrimitiveValueKey {
131 switch v.Type() {
132 case ldvalue.BoolType:
133 return jsonPrimitiveValueKey{valueType: ldvalue.BoolType, booleanValue: v.BoolValue()}
134 case ldvalue.NumberType:
135 return jsonPrimitiveValueKey{valueType: ldvalue.NumberType, numberValue: v.Float64Value()}
136 case ldvalue.StringType:
137 return jsonPrimitiveValueKey{valueType: ldvalue.StringType, stringValue: v.StringValue()}
138 default:
139 return jsonPrimitiveValueKey{}
140 }
141 }
142
143 func preprocessStringSet(valuesIn []string) map[string]struct{} {
144 if len(valuesIn) == 0 {
145 return nil
146 }
147 ret := make(map[string]struct{}, len(valuesIn))
148 for _, value := range valuesIn {
149 ret[value] = struct{}{}
150 }
151 return ret
152 }
153
154 func preprocessValues(
155 valuesIn []ldvalue.Value,
156 fn func(ldvalue.Value) clausePreprocessedValue,
157 ) []clausePreprocessedValue {
158 ret := make([]clausePreprocessedValue, len(valuesIn))
159 for i, v := range valuesIn {
160 p := fn(v)
161 p.computed = true
162 ret[i] = p
163 }
164 return ret
165 }
166
View as plain text