| 1 | module fsm |
| 2 | |
| 3 | pub type EventHandlerFn = fn (receiver voidptr, from string, to string) |
| 4 | |
| 5 | pub type ConditionFn = fn (receiver voidptr, from string, to string) bool |
| 6 | |
| 7 | fn dummy_event_handler_fn(_ voidptr, _ string, _ string) { |
| 8 | } |
| 9 | |
| 10 | fn dummy_condition_fn(_ voidptr, _ string, _ string) bool { |
| 11 | return true |
| 12 | } |
| 13 | |
| 14 | struct State { |
| 15 | mut: |
| 16 | entry_handler EventHandlerFn = dummy_event_handler_fn |
| 17 | run_handler EventHandlerFn = dummy_event_handler_fn |
| 18 | exit_handler EventHandlerFn = dummy_event_handler_fn |
| 19 | } |
| 20 | |
| 21 | struct Transition { |
| 22 | mut: |
| 23 | to string |
| 24 | condition_handler ConditionFn = dummy_condition_fn |
| 25 | } |
| 26 | |
| 27 | pub struct StateMachine { |
| 28 | mut: |
| 29 | states map[string]State |
| 30 | transitions map[string][]Transition |
| 31 | current_state string |
| 32 | } |
| 33 | |
| 34 | // StateMachine static method returns a new StateMachine instance. |
| 35 | pub fn new() StateMachine { |
| 36 | return StateMachine{} |
| 37 | } |
| 38 | |
| 39 | // set_state sets the current state of the state machine to the given state by `name`. |
| 40 | pub fn (mut s StateMachine) set_state(name string) ! { |
| 41 | if name in s.states { |
| 42 | s.current_state = name |
| 43 | } else { |
| 44 | return error('unknown state: ${name}') |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // get_state returns the current state of the state machine. |
| 49 | pub fn (mut s StateMachine) get_state() string { |
| 50 | return s.current_state |
| 51 | } |
| 52 | |
| 53 | // add_state adds a new state to the state machine. |
| 54 | // It takes the `name` of the state, and three event handlers: `entry`, `run`, and `exit`. |
| 55 | pub fn (mut s StateMachine) add_state(name string, entry EventHandlerFn, run EventHandlerFn, exit EventHandlerFn) { |
| 56 | s.states[name] = State{ |
| 57 | entry_handler: entry |
| 58 | run_handler: run |
| 59 | exit_handler: exit |
| 60 | } |
| 61 | if s.states.len == 1 { |
| 62 | s.current_state = name |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // add_transition adds a new transition to the state machine. |
| 67 | // It takes the `from` and `to` states, and a condition handler. |
| 68 | pub fn (mut s StateMachine) add_transition(from string, to string, condition_handler ConditionFn) { |
| 69 | t := Transition{ |
| 70 | to: to |
| 71 | condition_handler: condition_handler |
| 72 | } |
| 73 | if from in s.transitions { |
| 74 | s.transitions[from] << t |
| 75 | return |
| 76 | } |
| 77 | s.transitions[from] = [t] |
| 78 | } |
| 79 | |
| 80 | // run runs the state machine. It takes a `receiver` argument that is passed to the event handlers. |
| 81 | pub fn (mut s StateMachine) run(receiver voidptr) ! { |
| 82 | from_state := s.current_state |
| 83 | mut to_state := s.current_state |
| 84 | if transitions := s.transitions[s.current_state] { |
| 85 | for transition in transitions { |
| 86 | if transition.condition_handler(receiver, from_state, transition.to) { |
| 87 | s.change_state(receiver, transition.to) |
| 88 | to_state = transition.to |
| 89 | break |
| 90 | } |
| 91 | } |
| 92 | } else { |
| 93 | s.states[s.current_state].run_handler(receiver, from_state, to_state) |
| 94 | return error('no more transitions') |
| 95 | } |
| 96 | s.states[s.current_state].run_handler(receiver, from_state, to_state) |
| 97 | } |
| 98 | |
| 99 | fn (mut s StateMachine) change_state(receiver voidptr, newstate string) { |
| 100 | s.states[s.current_state].exit_handler(receiver, s.current_state, newstate) |
| 101 | s.states[newstate].entry_handler(receiver, s.current_state, newstate) |
| 102 | s.current_state = newstate |
| 103 | } |
| 104 | |