-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathops_test.go
115 lines (101 loc) · 3.35 KB
/
ops_test.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
package jsonlogic_test
import (
"context"
"encoding/json"
"fmt"
"regexp"
"github.com/QubitProducts/jsonlogic"
)
func init() {
}
// ExampleOpsSet demonstrates extending the set of available operations.
//
// Operations are collected together in an OpSet that can be asked to compile a
// given clause. Each operator (op) supported by a given OpSet must implement
// a BuildArgFunc that compiles that operation and returns the resulting
// ClauseFunc that will execute the operation at evaluation time. The build
// function is usually devided into two stages.
//
// At Compile time we check we have the correct number of arguments, and deal
// with errors and boundary conditions. We then call the BuildArgFunc for each
// argument that we would like to use at Evaluation time. The compile time stage
// then returns a closure over the arg functions we collect as a ClausFunc, that
// does the Exectution time evaluation.
//
// At Execution time, we call the claus functions we collected in the closure to
// retrieve the result of the argument for this particular call, and return
// and answer as neccessary.
//
// The following example implements a regular expression match operation.
func ExampleOpsSet() {
buildMatchOp := func(args jsonlogic.Arguments, ops jsonlogic.OpsSet) (jsonlogic.ClauseFunc, error) {
// We want two args, 1 for the data we are going to match, and one
// for the regex itself.
if len(args) < 2 {
return func(ctx context.Context, data interface{}) interface{} {
return false
}, nil
}
// We build a function for the data the user
// gives us to match against.
lArg, err := jsonlogic.BuildArgFunc(args[0], ops)
if err != nil {
return nil, err
}
// We build a function for the second argument that
// we hope will become a string to can compile to
// a regexp.
rArg, err := jsonlogic.BuildArgFunc(args[1], ops)
if err != nil {
return nil, err
}
return func(ctx context.Context, data interface{}) interface{} {
// We evaluate the first argument, using
// the data the user provided.
lval := lArg(ctx, data)
lstr, ok := lval.(string)
if !ok {
// We only match against strings, everything else
// is false.
return false
}
// We evaluate the second argument, using
// the data the user provided.
rval := rArg(ctx, data)
rstr, ok := rval.(string)
if !ok {
// We can only build regexp out of strings.
return false
}
// we compile oour string regexp (this could be cached).
rx, err := regexp.Compile(rstr)
if err != nil {
return false // JsonLogic never errors, bad things return false
}
// Finally we call out string match and return the answer
// we shoud on return string, float64, bool, []map[string]interface{},
// []interface{} (where the interfaces only consist of the same set of
// types.
return rx.MatchString(lstr)
}, nil
}
// Add our function to an OpSet
ops := jsonlogic.DefaultOps
ops["match"] = buildMatchOp
cls := jsonlogic.Clause{}
_ = json.Unmarshal([]byte(`{"match": [{"var":""},"this"]}`), &cls)
cf, _ := ops.Compile(&cls)
var tests = []interface{}{
`this matches`,
`that doesn't`,
float64(1),
}
ctx := context.Background()
for _, t := range tests {
fmt.Printf("match(%#v) = %v\n", t, jsonlogic.IsTrue(cf(ctx, t)))
}
// Output:
// match("this matches") = true
// match("that doesn't") = false
// match(1) = false
}