104 lines
2.4 KiB
Go
104 lines
2.4 KiB
Go
package workflow
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
var (
|
|
// ErrTransitionNotFound error if no transition with this name
|
|
ErrTransitionNotFound = errors.New("transition not found")
|
|
)
|
|
|
|
// Workflow state machine
|
|
type Workflow struct {
|
|
transitions map[Place][]Place
|
|
initialPlace Place
|
|
}
|
|
|
|
// NewWorkflow returns new Workflow instance
|
|
func NewWorkflow(initialPlace Place) *Workflow {
|
|
return &Workflow{initialPlace: initialPlace, transitions: map[Place][]Place{}}
|
|
}
|
|
|
|
// Can returns nil if transition applicable to object and error if not
|
|
func (w *Workflow) Can(obj Placeer, to Place) error {
|
|
currentPlace := obj.GetPlace()
|
|
if currentPlace == "" {
|
|
currentPlace = w.initialPlace
|
|
}
|
|
tr, ok := w.transitions[currentPlace]
|
|
if !ok {
|
|
return ErrTransitionNotFound
|
|
}
|
|
for _, f := range tr {
|
|
if f == to {
|
|
return nil
|
|
}
|
|
}
|
|
return ErrTransitionNotFound
|
|
}
|
|
|
|
// GetEnabledTransitions return all applicable transitions for object
|
|
func (w *Workflow) GetEnabledTransitions(obj Placeer) []Place {
|
|
currentPlace := obj.GetPlace()
|
|
if currentPlace == "" {
|
|
currentPlace = w.initialPlace
|
|
}
|
|
if _, ok := w.transitions[currentPlace]; !ok {
|
|
return nil
|
|
}
|
|
return w.transitions[currentPlace]
|
|
}
|
|
|
|
// Apply next state from transition to object
|
|
func (w *Workflow) Apply(obj Placeer, to Place) error {
|
|
currentPlace := obj.GetPlace()
|
|
if currentPlace == "" {
|
|
currentPlace = w.initialPlace
|
|
}
|
|
tr, ok := w.transitions[currentPlace]
|
|
if !ok {
|
|
return ErrTransitionNotFound
|
|
}
|
|
for _, f := range tr {
|
|
if f == to {
|
|
return obj.SetPlace(to)
|
|
}
|
|
}
|
|
return ErrTransitionNotFound
|
|
}
|
|
|
|
// AddTransition to workflow
|
|
func (w *Workflow) AddTransition(from Place, to Place) {
|
|
if _, ok := w.transitions[from]; !ok {
|
|
w.transitions[from] = []Place{}
|
|
}
|
|
w.transitions[from] = append(w.transitions[from], 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 from, to := range w.transitions {
|
|
for _, place := range to {
|
|
_, _ = buf.WriteString(fmt.Sprintf("%s -> %s[label=\"%s\"];\n", from, place, fmt.Sprintf("%s → %s", from, place)))
|
|
}
|
|
}
|
|
buf.WriteString("}")
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// Merge another workflow to current
|
|
func (w *Workflow) Merge(workflow *Workflow) {
|
|
for from, tos := range workflow.transitions {
|
|
for _, to := range tos {
|
|
w.AddTransition(from, to)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Place is one of state
|
|
type Place string
|