Guest User

Untitled

a guest
Jan 16th, 2019
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.47 KB | None | 0 0
  1. package fsm
  2.  
  3. import (
  4. "context"
  5. "reflect"
  6. "runtime"
  7. "strings"
  8. )
  9.  
  10. // StateFunc function that should return next state func or nil
  11. type StateFunc func() StateFunc
  12.  
  13. // NewFSM constructor func
  14. func NewFSM(
  15. beforeTrigger func(previousState, currentState string),
  16. afterTrigger func(currentStat, nextState string),
  17. ) *Fsm {
  18. return &Fsm{
  19. done: make(chan struct{}, 0),
  20. state: make(chan StateFunc, 1),
  21. stack: make([]StateFunc, 0),
  22. beforeTrigger: beforeTrigger,
  23. afterTrigger: afterTrigger,
  24. }
  25. }
  26.  
  27. // Fsm is a Final State Machine
  28. type Fsm struct {
  29. done chan struct{}
  30. state chan StateFunc
  31. current StateFunc
  32. stack []StateFunc
  33. beforeTrigger func(previousState, currentState string)
  34. afterTrigger func(currentStat, nextState string)
  35. }
  36.  
  37. // Start called to start fsm loop with init state
  38. func (fsm *Fsm) Start(ctx context.Context, state StateFunc) error {
  39. defer close(fsm.done)
  40.  
  41. fsm.state <- state
  42. for {
  43.  
  44. if ctx.Err() != nil {
  45. return nil
  46. }
  47.  
  48. select {
  49. case newState := <-fsm.state:
  50. // nil means that state ends and fsm should be returned to a previous state
  51. if newState == nil {
  52. newState = fsm.popState()
  53. // exit if there are no previous state in the stack
  54. if newState == nil {
  55. return nil
  56. }
  57. }
  58.  
  59. // call a before action trigger
  60. if fsm.beforeTrigger != nil {
  61. fsm.beforeTrigger(getStateName(fsm.current), getStateName(newState))
  62. }
  63.  
  64. fsm.current = newState
  65.  
  66. // action
  67. nextState := newState()
  68.  
  69. fsm.state <- nextState
  70.  
  71. // call an after action trigger
  72. if fsm.afterTrigger != nil {
  73. fsm.afterTrigger(getStateName(newState), getStateName(nextState))
  74. }
  75. case <-ctx.Done():
  76. return nil
  77. }
  78. }
  79.  
  80. }
  81.  
  82. func (fsm *Fsm) Done() <-chan struct{} {
  83. return fsm.done
  84. }
  85.  
  86. // PushState used to push a new state into stack
  87. func (fsm *Fsm) PushState(state StateFunc) StateFunc {
  88. fsm.stack = append(fsm.stack, fsm.current)
  89. return state
  90. }
  91.  
  92. // popState used to pop previous state from the stack
  93. func (fsm *Fsm) popState() StateFunc {
  94. if len(fsm.stack) == 0 {
  95. return nil
  96. }
  97.  
  98. state := fsm.stack[len(fsm.stack)-1]
  99. fsm.stack = fsm.stack[:len(fsm.stack)-1]
  100. return state
  101. }
  102.  
  103. func getStateName(f StateFunc) string {
  104. if f == nil {
  105. return ""
  106. }
  107.  
  108. fullName := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
  109. if fullName == "" {
  110. return ""
  111. }
  112.  
  113. lastIndex1 := strings.LastIndex(fullName, ".")
  114. lastIndex2 := strings.LastIndex(fullName, "-")
  115.  
  116. return strings.Replace(fullName[lastIndex1+1:lastIndex2], ")", "", -1)
  117. }
Add Comment
Please, Sign In to add comment