package state import ( "log" "alin.ovh/erl/command" ) type State int const ( NotStarted State = iota Running Exited ) func (s State) String() string { switch s { case NotStarted: return "NotStarted" case Running: return "Running" case Exited: return "Exited" default: return "Unknown" } } type Event int const ( Start Event = iota Signal Exit Restart ) func (e Event) String() string { switch e { case Start: return "Start" case Signal: return "Shutdown" case Exit: return "Stopped" case Restart: return "Restart" default: return "Unknown" } } type Action func() type StateMachine struct { log *log.Logger currentState State transitions map[State]map[Event]State actions map[State]map[Event]Action } func New(cmd command.Command, log *log.Logger) *StateMachine { sm := &StateMachine{ log: log, currentState: NotStarted, transitions: make(map[State]map[Event]State), actions: make(map[State]map[Event]Action), } sm.transitions[NotStarted] = map[Event]State{ Start: Running, } sm.transitions[Running] = map[Event]State{ Signal: Exited, Exit: Exited, Restart: Running, } sm.transitions[Exited] = map[Event]State{ Start: Running, Restart: Running, } wait := func(cmd command.Command) { err := cmd.Wait() if err != nil { log.Printf("Error waiting for command: %v\n", err) } sm.SendEvent(Exit) } start := func() { err := cmd.Start() if err != nil { log.Printf("Failed to start command: %v", err) } go wait(cmd) } stop := func() { err := cmd.Stop() if err != nil { log.Printf("Error stopping command: %v\n", err) } } restart := func() { stop() start() } sm.actions[NotStarted] = map[Event]Action{ Start: start, } sm.actions[Running] = map[Event]Action{ Signal: stop, Restart: restart, } sm.actions[Exited] = map[Event]Action{ Start: start, Restart: start, } return sm } func (sm *StateMachine) SendEvent(ev Event) { currentState := sm.currentState if nextState, ok := sm.transitions[currentState][ev]; ok { sm.currentState = nextState if action, ok := sm.actions[currentState][ev]; ok { action() } } }