1 package gval
2
3 import (
4 "context"
5 "fmt"
6 "reflect"
7 "regexp"
8 "strconv"
9 "strings"
10 )
11
12
13
14
15 type Selector interface {
16 SelectGVal(c context.Context, key string) (interface{}, error)
17 }
18
19
20 type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)
21
22
23 func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error) {
24 v, err := e(c, parameter)
25 if err != nil {
26 return 0, err
27 }
28
29 f, ok := convertToFloat(v)
30 if !ok {
31 return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
32 }
33 return int(f), nil
34 }
35
36
37 func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error) {
38 v, err := e(c, parameter)
39 if err != nil {
40 return 0, err
41 }
42
43 f, ok := convertToFloat(v)
44 if !ok {
45 return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
46 }
47 return f, nil
48 }
49
50
51 func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error) {
52 v, err := e(c, parameter)
53 if err != nil {
54 return false, err
55 }
56
57 b, ok := convertToBool(v)
58 if !ok {
59 return false, fmt.Errorf("expected bool but got %v (%T)", v, v)
60 }
61 return b, nil
62 }
63
64
65 func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error) {
66 o, err := e(c, parameter)
67 if err != nil {
68 return "", err
69 }
70 return fmt.Sprintf("%v", o), nil
71 }
72
73
74 func (*Parser) Const(value interface{}) Evaluable {
75 return constant(value)
76 }
77
78
79 func constant(value interface{}) Evaluable {
80 return func(c context.Context, v interface{}) (interface{}, error) {
81 return value, nil
82 }
83 }
84
85
86
87
88
89
90
91
92
93
94 func (p *Parser) Var(path ...Evaluable) Evaluable {
95 if p.selector == nil {
96 return variable(path)
97 }
98 return p.selector(path)
99 }
100
101
102 type Evaluables []Evaluable
103
104
105 func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error) {
106 strs := make([]string, len(evs))
107 for i, p := range evs {
108 k, err := p.EvalString(c, parameter)
109 if err != nil {
110 return nil, err
111 }
112 strs[i] = k
113 }
114 return strs, nil
115 }
116
117 func variable(path Evaluables) Evaluable {
118 return func(c context.Context, v interface{}) (interface{}, error) {
119 keys, err := path.EvalStrings(c, v)
120 if err != nil {
121 return nil, err
122 }
123 for i, k := range keys {
124 switch o := v.(type) {
125 case Selector:
126 v, err = o.SelectGVal(c, k)
127 if err != nil {
128 return nil, fmt.Errorf("failed to select '%s' on %T: %w", k, o, err)
129 }
130 continue
131 case map[interface{}]interface{}:
132 v = o[k]
133 continue
134 case map[string]interface{}:
135 v = o[k]
136 continue
137 case []interface{}:
138 if i, err := strconv.Atoi(k); err == nil && i >= 0 && len(o) > i {
139 v = o[i]
140 continue
141 }
142 default:
143 var ok bool
144 v, ok = reflectSelect(k, o)
145 if !ok {
146 return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
147 }
148 }
149 }
150 return v, nil
151 }
152 }
153
154 func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
155 vv := reflect.ValueOf(value)
156 vvElem := resolvePotentialPointer(vv)
157
158 switch vvElem.Kind() {
159 case reflect.Map:
160 mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
161 if !ok {
162 return nil, false
163 }
164
165 vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
166 vvElem = resolvePotentialPointer(vvElem)
167
168 if vvElem.IsValid() {
169 return vvElem.Interface(), true
170 }
171 case reflect.Slice:
172 if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
173 vvElem = resolvePotentialPointer(vv.Index(i))
174 return vvElem.Interface(), true
175 }
176 case reflect.Struct:
177 field := vvElem.FieldByName(key)
178 if field.IsValid() {
179 return field.Interface(), true
180 }
181
182 method := vv.MethodByName(key)
183 if method.IsValid() {
184 return method.Interface(), true
185 }
186 }
187 return nil, false
188 }
189
190 func resolvePotentialPointer(value reflect.Value) reflect.Value {
191 if value.Kind() == reflect.Ptr {
192 return value.Elem()
193 }
194 return value
195 }
196
197 func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
198 switch k {
199 case reflect.String:
200 return value, true
201 case reflect.Int:
202 if i, err := strconv.Atoi(value); err == nil {
203 return i, true
204 }
205 }
206 return nil, false
207 }
208
209 func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
210 return func(c context.Context, v interface{}) (ret interface{}, err error) {
211 a := make([]interface{}, len(args))
212 for i, arg := range args {
213 ai, err := arg(c, v)
214 if err != nil {
215 return nil, err
216 }
217 a[i] = ai
218 }
219 return fun(c, a...)
220 }
221 }
222
223 func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable) Evaluable {
224 return func(c context.Context, v interface{}) (ret interface{}, err error) {
225 f, err := fun(c, v)
226
227 if err != nil {
228 return nil, fmt.Errorf("could not call function: %w", err)
229 }
230
231 defer func() {
232 if r := recover(); r != nil {
233 err = fmt.Errorf("failed to execute function '%s': %s", fullname, r)
234 ret = nil
235 }
236 }()
237
238 ff := reflect.ValueOf(f)
239
240 if ff.Kind() != reflect.Func {
241 return nil, fmt.Errorf("could not call '%s' type %T", fullname, f)
242 }
243
244 a := make([]reflect.Value, len(args))
245 for i := range args {
246 arg, err := args[i](c, v)
247 if err != nil {
248 return nil, err
249 }
250 a[i] = reflect.ValueOf(arg)
251 }
252
253 rr := ff.Call(a)
254
255 r := make([]interface{}, len(rr))
256 for i, e := range rr {
257 r[i] = e.Interface()
258 }
259
260 errorInterface := reflect.TypeOf((*error)(nil)).Elem()
261 if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
262 if r[len(r)-1] != nil {
263 err = r[len(r)-1].(error)
264 }
265 r = r[0 : len(r)-1]
266 }
267
268 switch len(r) {
269 case 0:
270 return err, nil
271 case 1:
272 return r[0], err
273 default:
274 return r, err
275 }
276 }
277 }
278
279
280 func (e Evaluable) IsConst() bool {
281 pc := reflect.ValueOf(constant(nil)).Pointer()
282 pe := reflect.ValueOf(e).Pointer()
283 return pc == pe
284 }
285
286 func regEx(a, b Evaluable) (Evaluable, error) {
287 if !b.IsConst() {
288 return func(c context.Context, o interface{}) (interface{}, error) {
289 a, err := a.EvalString(c, o)
290 if err != nil {
291 return nil, err
292 }
293 b, err := b.EvalString(c, o)
294 if err != nil {
295 return nil, err
296 }
297 matched, err := regexp.MatchString(b, a)
298 return matched, err
299 }, nil
300 }
301 s, err := b.EvalString(context.TODO(), nil)
302 if err != nil {
303 return nil, err
304 }
305 regex, err := regexp.Compile(s)
306 if err != nil {
307 return nil, err
308 }
309 return func(c context.Context, v interface{}) (interface{}, error) {
310 s, err := a.EvalString(c, v)
311 if err != nil {
312 return nil, err
313 }
314 return regex.MatchString(s), nil
315 }, nil
316 }
317
318 func notRegEx(a, b Evaluable) (Evaluable, error) {
319 if !b.IsConst() {
320 return func(c context.Context, o interface{}) (interface{}, error) {
321 a, err := a.EvalString(c, o)
322 if err != nil {
323 return nil, err
324 }
325 b, err := b.EvalString(c, o)
326 if err != nil {
327 return nil, err
328 }
329 matched, err := regexp.MatchString(b, a)
330 return !matched, err
331 }, nil
332 }
333 s, err := b.EvalString(context.TODO(), nil)
334 if err != nil {
335 return nil, err
336 }
337 regex, err := regexp.Compile(s)
338 if err != nil {
339 return nil, err
340 }
341 return func(c context.Context, v interface{}) (interface{}, error) {
342 s, err := a.EvalString(c, v)
343 if err != nil {
344 return nil, err
345 }
346 return !regex.MatchString(s), nil
347 }, nil
348 }
349
View as plain text