diff options
author | Alexander Kiryukhin <a.kiryukhin@mail.ru> | 2019-12-08 13:40:57 +0300 |
---|---|---|
committer | Alexander Kiryukhin <a.kiryukhin@mail.ru> | 2019-12-08 13:40:57 +0300 |
commit | c8749e6f6bf8f39a94791526b39fadc11360476c (patch) | |
tree | e6a8cfff3afe8750b5452a31755060f07eb99502 /workflow.go |
Initial
Diffstat (limited to 'workflow.go')
-rw-r--r-- | workflow.go | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/workflow.go b/workflow.go new file mode 100644 index 0000000..8911cec --- /dev/null +++ b/workflow.go @@ -0,0 +1,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 +} |