-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsm.go
80 lines (70 loc) · 2.16 KB
/
sm.go
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
package statemachine
import (
"context"
)
//ContextData because short of generic-types, use empty interface to implement
type ContextData interface{}
type Handler func(data ContextData)
type Transition struct {
FromSate string
ToState string
Event string
Processor EventProcessor
}
type EventProcessor interface {
//Before hanles before transition
Before(ctx context.Context, t *Transition, stateHolder StateHolder, ctxData ContextData) Error
// OnEvent handles to do all the transition
OnEvent(ctx context.Context, t *Transition, stateHolder StateHolder, ctxData ContextData) Error
//After handles things after transition
After(ctx context.Context, t *Transition, stateHolder StateHolder, ctxData ContextData) Error
}
func FlowTemplate(ctx context.Context, t *Transition, stateHolder StateHolder, ctxData ContextData) Error {
var err Error
if err = t.Processor.Before(ctx, t, stateHolder, ctxData); err != nil {
return err
}
if err = t.Processor.OnEvent(ctx, t, stateHolder, ctxData); err != nil {
return err
}
if err = t.Processor.After(ctx, t, stateHolder, ctxData); err != nil {
return err
}
return nil
}
type StateMachine struct {
Name string
Transitions []*Transition
}
type StateHolder interface {
CurrentState() string
}
type Locker interface {
Lock(ctx context.Context) (StateHolder, error)
}
func NewStateMachine(name string, transitions []*Transition) *StateMachine {
return &StateMachine{
Name: name,
Transitions: transitions,
}
}
func (m *StateMachine) Trigger(ctx context.Context, event string, locker Locker, ctxData ContextData) Error {
stateHolder, err := locker.Lock(ctx)
if err != nil {
return &statemachineLockerError{locker: locker, cause: err, event: event}
}
var currentState = stateHolder.CurrentState()
transition := m.Find(currentState, event)
if transition == nil {
return &noTransitionError{statemachine: m.Name, event: event, currentState: currentState}
}
return FlowTemplate(ctx, transition, stateHolder, ctxData)
}
func (m *StateMachine) Find(from string, event string) *Transition {
for _, v := range m.Transitions {
if v.FromSate == from && v.Event == event {
return v
}
}
return nil
}