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
|
package workflow
import (
"bytes"
"errors"
"fmt"
)
var (
ErrCantApply = errors.New("cant apply transition")
ErrTransitionNotFound = errors.New("transition not found")
)
// Workflow state machine
type Workflow struct {
transitions map[string]transition
initialPlace Place
}
// NewWorkflow returns new Workflow instance
func NewWorkflow(initialPlace Place) *Workflow {
return &Workflow{initialPlace: initialPlace, transitions: map[string]transition{}}
}
// Can returns nil if transition applicable to object and error if not
func (w *Workflow) Can(obj Placeer, transition string) error {
currentPlace := obj.GetPlace()
if currentPlace == "" {
currentPlace = w.initialPlace
}
tr, ok := w.transitions[transition]
if !ok {
return ErrTransitionNotFound
}
for _, f := range tr.From {
if f == currentPlace {
return nil
}
}
return ErrCantApply
}
// GetEnabledTransitions return all applicable transitions for object
func (w *Workflow) GetEnabledTransitions(obj Placeer) []string {
currentPlace := obj.GetPlace()
if currentPlace == "" {
currentPlace = w.initialPlace
}
result := make([]string, 0)
for name, t := range w.transitions {
for _, f := range t.From {
if f == currentPlace {
result = append(result, name)
break
}
}
}
return result
}
// Apply next state from transition to object
func (w *Workflow) Apply(obj Placeer, transition string) error {
currentPlace := obj.GetPlace()
if currentPlace == "" {
currentPlace = w.initialPlace
}
tr, ok := w.transitions[transition]
if !ok {
return ErrTransitionNotFound
}
for _, f := range tr.From {
if f == currentPlace {
return obj.SetPlace(tr.To)
}
}
return ErrCantApply
}
// AddTransition to workflow
func (w *Workflow) AddTransition(name string, from []Place, to Place) {
w.transitions[name] = transition{
From: from,
To: to,
}
}
// DumpToDot dumps transitions to Graphviz Dot format
func (w *Workflow) DumpToDot() []byte {
buf := bytes.NewBufferString(fmt.Sprintf("digraph {\n%s[color=\"blue\"]\n", w.initialPlace))
for name, t := range w.transitions {
for _, f := range t.From {
_, _ = buf.WriteString(fmt.Sprintf("%s -> %s[label=\"%s\"];\n", f, t.To, name))
}
}
buf.WriteString("}")
return buf.Bytes()
}
// Place is one of state
type Place string
type transition struct {
From []Place
To Place
}
|