-
Notifications
You must be signed in to change notification settings - Fork 0
/
joins.go
126 lines (109 loc) · 2.58 KB
/
joins.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
package goraff
import "fmt"
// Condition is a condition that must be met for a join to be taken
type FollowIf interface {
Match(s *ReadableGraph) (bool, error)
}
// Manage joins
type Joins struct {
joins map[string][]*Join
Blocks *Blocks
errs []error
}
func (j *Joins) trackErr(err error) error {
j.errs = append(j.errs, err)
return err
}
func (j *Joins) Validate() error {
if j.errs != nil {
return fmt.Errorf("joins have errors: %v", j.errs)
}
return nil
}
type ErrBlockNotFound struct {
ID string
}
func (e ErrBlockNotFound) Error() string {
return "block not found: " + e.ID
}
func (j *Joins) Add(fromName, toName string, condition FollowIf) error {
if j.Blocks == nil {
return fmt.Errorf("joins must be associated with a Blocks struct")
}
from := j.Blocks.Get(fromName)
if from == nil {
return j.trackErr(ErrBlockNotFound{
ID: fromName,
})
}
to := j.Blocks.Get(toName)
if to == nil {
return j.trackErr(ErrBlockNotFound{
ID: toName,
})
}
if j.joins == nil {
j.joins = make(map[string][]*Join)
}
e := &Join{From: from, To: to, Condition: condition}
if _, ok := j.joins[fromName]; !ok {
j.joins[fromName] = []*Join{}
}
j.joins[fromName] = append(j.joins[fromName], e)
return nil
}
func (j *Joins) Get(from string) []*Join {
if _, ok := j.joins[from]; !ok {
return nil
}
return j.joins[from]
}
// Join connects two blocks in a scaff
type Join struct {
From *Block
To *Block
Condition FollowIf
}
func (e *Join) TriggersMet(s *ReadableGraph) (bool, error) {
if e.Condition == nil {
// without a conditon, we always trigger the join
return true, nil
}
return e.Condition.Match(s)
}
type followIfKeyMatchesName struct {
Name string
Key string
Value string
}
func (e *followIfKeyMatchesName) Match(s *ReadableGraph) (bool, error) {
n, err := s.FirstNodeByName(e.Name)
if err != nil {
return false, fmt.Errorf("error getting node state: %w", err)
}
return n.FirstStr(e.Key) == e.Value, nil
}
func FollowIfKeyMatches(nodeID, key, value string) FollowIf {
return &followIfKeyMatchesName{Name: nodeID, Key: key, Value: value}
}
type followIfNodesCompleted struct {
NodeIDs []string
}
func (e *followIfNodesCompleted) Match(s *ReadableGraph) (bool, error) {
for _, nodeID := range e.NodeIDs {
st, err := s.FirstNodeByName(nodeID)
if err != nil {
return false, fmt.Errorf("error getting node state: %w", err)
}
if st == nil {
return false, nil
}
if !st.Done() {
return false, nil
}
}
return true, nil
}
func FollowIfNodesCompleted(nodeIDs ...string) FollowIf {
return &followIfNodesCompleted{NodeIDs: nodeIDs}
}