State Machine is a Golang library implementing a finite-state machine.
Basic usage works as follows:
- Create a machine with an initial state.
- Set all possible transitions between states.
- Optional: Set enter end exit actions.
- Transition from state to state ;)
Here is an example code based on the very simple use-case of a door:
// Signature: <machine> = <initial state>
machine := fsm.NewMachine("close")
// Signature: <current state, input event, next state>
machine.SetTransition("open", "close-door", "close")
machine.SetTransition("close", "open-door", "open")
machine.SetEnterAction(func(last, new string) error {
fmt.Printf("Enter '%s' coming from '%s'\n", new, last)
return nil
})
machine.SetExitAction(func(current, next string) error {
fmt.Printf("Leaving '%s' going to '%s'\n", current, next)
return nil
})
fmt.Printf("Current state is %s\n", machine.State())
// Signature: <last, current, err> = <input event>
if _, _, err := machine.Transition("open-door"); err != nil {
panic(err)
}
fmt.Printf("Current state is %s\n", machine.State())
Define a struct that holds all the data you need. Here is an example containing the relevant changes based on the door example:
type DoorState struct {
Id string
OpenedBy string
}
machine := fsm.NewMachine(&DoorState{Id: "close"})
machine.SetTransition(&DoorState{Id: "open"}, "close-door", &DoorState{Id: "close"})
machine.SetTransition(&DoorState{Id: "close"}, "open-door", &DoorState{Id: "open"})
machine.SetEnterAction(func(last, new *DoorState) error {
new.OpenedBy = "Peter"
return nil
})
// ...
When working with complex states, there two important notes to consider:
- Equality of states is checked with
reflect.DeepEqual
. This works both for values and pointers. - When changing a state's fields in an action function, a pointer must be used. Otherwise, the change will be lost.