graphql-engine/cli/internal/fsm/fsm.go
Mohd Bilal 4c24e6d56b cli: refactor internal/fsm to use internal/errors
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6694
GitOrigin-RevId: 96938d65f8de857c87e37b7d0fc508cee6436340
2022-11-10 14:58:49 +00:00

77 lines
1.5 KiB
Go

package fsm
import (
"fmt"
"sync"
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
)
var ErrEventRejected = fmt.Errorf("event rejected")
const (
Default StateType = ""
NoOp EventType = "NoOp"
)
type StateType string
type EventType string
type EventContext interface{}
type Action interface {
Execute(eventCtx EventContext) EventType
}
type Events map[EventType]StateType
type State struct {
Action Action
Events Events
}
type States map[StateType]State
type StateMachine struct {
Previous StateType
Current StateType
States States
mutex sync.Mutex
}
func (s *StateMachine) getNextState(event EventType) (StateType, error) {
var op errors.Op = "fsm.StateMachine.getNextState"
if state, ok := s.States[s.Current]; ok {
if state.Events != nil {
if next, ok := state.Events[event]; ok {
return next, nil
}
}
}
return Default, errors.E(op, fmt.Errorf("next state: %w: %s", ErrEventRejected, event))
}
func (s *StateMachine) SendEvent(event EventType, eventCtx EventContext) error {
var op errors.Op = "fsm.StateMachine.SendEvent"
s.mutex.Lock()
defer s.mutex.Unlock()
for {
nextState, err := s.getNextState(event)
if err != nil {
return errors.E(op, fmt.Errorf("%w: %s", err, event))
}
state, ok := s.States[nextState]
if !ok || state.Action == nil {
return errors.E(op, fmt.Errorf("config error"))
}
s.Previous = s.Current
s.Current = nextState
nextEvent := state.Action.Execute(eventCtx)
if nextEvent == NoOp {
return nil
}
event = nextEvent
}
}