forked from open-policy-agent/opa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
builtins.go
149 lines (130 loc) · 4.81 KB
/
builtins.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
// Copyright 2016 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package topdown
import (
"fmt"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/topdown/builtins"
)
type (
// FunctionalBuiltin1 defines an interface for simple functional built-ins.
//
// Implement this interface if your built-in function takes one input and
// produces one output.
//
// If an error occurs, the functional built-in should return a descriptive
// message. The message should not be prefixed with the built-in name as the
// framework takes care of this.
FunctionalBuiltin1 func(op1 ast.Value) (output ast.Value, err error)
// FunctionalBuiltin2 defines an interface for simple functional built-ins.
//
// Implement this interface if your built-in function takes two inputs and
// produces one output.
//
// If an error occurs, the functional built-in should return a descriptive
// message. The message should not be prefixed with the built-in name as the
// framework takes care of this.
FunctionalBuiltin2 func(op1, op2 ast.Value) (output ast.Value, err error)
// FunctionalBuiltin3 defines an interface for simple functional built-ins.
//
// Implement this interface if your built-in function takes three inputs and
// produces one output.
//
// If an error occurs, the functional built-in should return a descriptive
// message. The message should not be prefixed with the built-in name as the
// framework takes care of this.
FunctionalBuiltin3 func(op1, op2, op3 ast.Value) (output ast.Value, err error)
// BuiltinContext contains context from the evaluator that may be used by
// built-in functions.
BuiltinContext struct {
Cache builtins.Cache
Location *ast.Location
}
// BuiltinFunc defines an interface for implementing built-in functions.
// The built-in function is called with the plugged operands from the call
// (including the output operands.) The implementation should evaluate the
// operands and invoke the iteraror for each successful/defined output
// value.
BuiltinFunc func(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error
)
// RegisterBuiltinFunc adds a new built-in function to the evaluation engine.
func RegisterBuiltinFunc(name string, f BuiltinFunc) {
builtinFunctions[name] = f
}
// RegisterFunctionalBuiltin1 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1) {
builtinFunctions[name] = functionalWrapper1(name, fun)
}
// RegisterFunctionalBuiltin2 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2) {
builtinFunctions[name] = functionalWrapper2(name, fun)
}
// RegisterFunctionalBuiltin3 adds a new built-in function to the evaluation
// engine.
func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3) {
builtinFunctions[name] = functionalWrapper3(name, fun)
}
// BuiltinEmpty is used to signal that the built-in function evaluated, but the
// result is undefined so evaluation should not continue.
type BuiltinEmpty struct{}
func (BuiltinEmpty) Error() string {
return "<empty>"
}
var builtinFunctions = map[string]BuiltinFunc{}
func functionalWrapper1(name string, fn FunctionalBuiltin1) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
if _, empty := err.(BuiltinEmpty); empty {
return nil
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func functionalWrapper2(name string, fn FunctionalBuiltin2) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value, args[1].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
if _, empty := err.(BuiltinEmpty); empty {
return nil
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func functionalWrapper3(name string, fn FunctionalBuiltin3) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value, args[1].Value, args[2].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
if _, empty := err.(BuiltinEmpty); empty {
return nil
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func handleBuiltinErr(name string, loc *ast.Location, err error) error {
switch err := err.(type) {
case BuiltinEmpty:
return nil
case builtins.ErrOperand:
return &Error{
Code: TypeErr,
Message: fmt.Sprintf("%v: %v", string(name), err.Error()),
Location: loc,
}
default:
return &Error{
Code: InternalErr,
Message: fmt.Sprintf("%v: %v", string(name), err.Error()),
Location: loc,
}
}
}