forked from open-policy-agent/opa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreachable.go
61 lines (52 loc) · 1.63 KB
/
reachable.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
// Copyright 2020 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 (
"github.com/open-policy-agent/opa/ast"
)
// Helper: sets of vertices can be represented as Arrays or Sets.
func foreachVertex(collection *ast.Term, f func(*ast.Term)) {
switch v := collection.Value.(type) {
case ast.Set:
v.Foreach(f)
case *ast.Array:
v.Foreach(f)
}
}
func builtinReachable(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
// Return the empty set if the first argument is not an object.
graph, ok := args[0].Value.(ast.Object)
if !ok {
return iter(ast.NewTerm(ast.NewSet()))
}
// This is a queue that holds all nodes we still need to visit. It is
// initialised to the initial set of nodes we start out with.
queue := []*ast.Term{}
foreachVertex(args[1], func(t *ast.Term) {
queue = append(queue, t)
})
// This is the set of nodes we have reached.
reached := ast.NewSet()
// Keep going as long as we have nodes in the queue.
for len(queue) > 0 {
// Get the edges for this node. If the node was not in the graph,
// `edges` will be `nil` and we can ignore it.
node := queue[0]
if edges := graph.Get(node); edges != nil {
// Add all the newly discovered neighbors.
foreachVertex(edges, func(neighbor *ast.Term) {
if !reached.Contains(neighbor) {
queue = append(queue, neighbor)
}
})
// Mark the node as reached.
reached.Add(node)
}
queue = queue[1:]
}
return iter(ast.NewTerm(reached))
}
func init() {
RegisterBuiltinFunc(ast.ReachableBuiltin.Name, builtinReachable)
}