workflow/workflow.go
Alexander Kiryukhin 2b10929c48 Added merge
2020-07-15 16:08:05 +03:00

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