-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsonlogic.go
166 lines (146 loc) · 3.54 KB
/
jsonlogic.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
package jsonlogic
import (
"fmt"
)
var (
// DefaultJSONLogic is the JSONLogic instance used by package-level Apply/AddOperation.
DefaultJSONLogic = New()
)
// JSONLogic is an evaluator of json logic with a set of operations.
type JSONLogic struct {
parent *JSONLogic
ops map[string]Operation
}
type Applier func(logic, data interface{}) (res interface{}, err error)
type Operation func(apply Applier, params []interface{}, data interface{}) (interface{}, error)
// NewInherit creates a child JSONLogic instance.
func NewInherit(parent *JSONLogic) *JSONLogic {
return &JSONLogic{
parent: parent,
ops: make(map[string]Operation),
}
}
// New creates a root (no parent) JSONLogic with standard operations.
func New() *JSONLogic {
ret := NewEmpty()
// Data access.
AddOpVar(ret)
AddOpMissing(ret)
AddOpMissingSome(ret)
// Logic. XXX: "=="/"!=" not supported, only support "==="/"!=="
AddOpIf(ret)
AddOpStrictEqual(ret)
AddOpStrictNotEqual(ret)
AddOpNegative(ret)
AddOpDoubleNegative(ret)
AddOpAnd(ret)
AddOpOr(ret)
// Numeric.
AddOpLessThan(ret)
AddOpLessEqual(ret)
AddOpGreaterThan(ret)
AddOpGreaterEqual(ret)
AddOpMin(ret)
AddOpMax(ret)
AddOpAdd(ret)
AddOpMul(ret)
AddOpMinus(ret)
AddOpDiv(ret)
AddOpMod(ret)
// Array and string.
AddOpMap(ret)
AddOpFilter(ret)
AddOpReduce(ret)
AddOpAll(ret)
AddOpNone(ret)
AddOpSome(ret)
AddOpMerge(ret)
AddOpIn(ret)
AddOpCat(ret)
AddOpSubstr(ret)
return ret
}
// NewEmpty creates a root (no parent) JSONLogic with no operation.
func NewEmpty() *JSONLogic {
return &JSONLogic{
ops: make(map[string]Operation),
}
}
// Apply is equivalent to DefaultJSONLogic.Apply.
func Apply(logic, data interface{}) (res interface{}, err error) {
return DefaultJSONLogic.Apply(logic, data)
}
// Apply data to logic and returns a result. Both logic/data must be one of 'encoding/json' supported types:
// - nil
// - bool
// - float64
// - string
// - []interface{} with items of supported types
// - map[string]interface{} with values of supported types
func (jl *JSONLogic) Apply(logic, data interface{}) (res interface{}, err error) {
// An array of rules.
if arr, ok := logic.([]interface{}); ok {
ret := []interface{}{}
for _, item := range arr {
res, err := jl.Apply(item, data)
if err != nil {
return nil, err
}
ret = append(ret, res)
}
return ret, nil
}
// Primitive.
if !isLogic(logic) {
return logic, nil
}
if data == nil {
data = map[string]interface{}{}
}
op, params := getLogic(logic)
defer func() {
if e := recover(); e != nil {
var ok bool
err, ok = e.(error)
if !ok {
err = fmt.Errorf("%v", e)
}
}
}()
var opFn Operation
for inst := jl; inst != nil; inst = inst.parent {
var ok bool
opFn, ok = inst.ops[op]
if ok {
break
}
}
if opFn == nil {
return nil, fmt.Errorf("Apply: operator %q not found", op)
}
return opFn(jl.Apply, params, data)
}
// AddOperation is equivalent to DefaultJSONLogic.AddOperation.
func AddOperation(name string, op Operation) {
DefaultJSONLogic.AddOperation(name, op)
}
// AddOperation adds a named operation to JSONLogic instance.
// Can override parent's same name operation.
func (jl *JSONLogic) AddOperation(name string, op Operation) {
jl.ops[name] = op
}
// Clone is equivalent to DefaultJSONLogic.Clone.
func Clone() *JSONLogic {
return DefaultJSONLogic.Clone()
}
// Clone clones a JSONLogic instance.
func (jl *JSONLogic) Clone() *JSONLogic {
ret := &JSONLogic{
parent: jl.parent,
ops: make(map[string]Operation),
}
for k, v := range jl.ops {
ret.ops[k] = v
}
return ret
}