From c8749e6f6bf8f39a94791526b39fadc11360476c Mon Sep 17 00:00:00 2001 From: Alexander Kiryukhin Date: Sun, 8 Dec 2019 13:40:57 +0300 Subject: Initial --- workflow.go | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 workflow.go (limited to 'workflow.go') 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 +} -- cgit v1.2.3