Documentation ¶
Overview ¶
Package fsm implements a finite state machine.
It is heavily based on two FSM implementations:
Javascript Finite State Machine https://github.com/jakesgordon/javascript-state-machine
Fysom for Python https://github.com/oxplot/fysom (forked at https://github.com/mriehl/fysom)
Index ¶
- func Visualize(fsm *FSM) string
- func VisualizeForMermaidWithGraphType(fsm *FSM, graphType MermaidDiagramType) (string, error)
- func VisualizeWithType(fsm *FSM, visualizeType VisualizeType) (string, error)
- type AsyncError
- type Callback
- type Callbacks
- type CanceledError
- type Event
- type EventDesc
- type Events
- type FSM
- func (f *FSM) AvailableTransitions() []string
- func (f *FSM) Can(event string) bool
- func (f *FSM) Cannot(event string) bool
- func (f *FSM) Current() string
- func (f *FSM) DeleteMetadata(key string)
- func (f *FSM) Event(ctx context.Context, event string, args ...interface{}) error
- func (f *FSM) Is(state string) bool
- func (f *FSM) Metadata(key string) (interface{}, bool)
- func (f *FSM) SetMetadata(key string, dataValue interface{})
- func (f *FSM) SetState(state string)
- func (f *FSM) Transition() error
- type InTransitionError
- type InternalError
- type InvalidEventError
- type MermaidDiagramType
- type NoTransitionError
- type NotInTransitionError
- type UnknownEventError
- type VisualizeType
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func VisualizeForMermaidWithGraphType ¶ added in v0.2.0
func VisualizeForMermaidWithGraphType(fsm *FSM, graphType MermaidDiagramType) (string, error)
VisualizeForMermaidWithGraphType outputs a visualization of a FSM in Mermaid format as specified by the graphType.
func VisualizeWithType ¶ added in v0.2.0
func VisualizeWithType(fsm *FSM, visualizeType VisualizeType) (string, error)
VisualizeWithType outputs a visualization of a FSM in the desired format. If the type is not given it defaults to GRAPHVIZ
Types ¶
type AsyncError ¶
AsyncError is returned by FSM.Event() when a callback have initiated an asynchronous state transition.
func (AsyncError) Error ¶
func (e AsyncError) Error() string
type Callback ¶
Callback is a function type that callbacks should use. Event is the current event info as the callback happens.
type CanceledError ¶
type CanceledError struct {
Err error
}
CanceledError is returned by FSM.Event() when a callback have canceled a transition.
func (CanceledError) Error ¶
func (e CanceledError) Error() string
type Event ¶
type Event struct { // FSM is an reference to the current FSM. FSM *FSM // Event is the event name. Event string // Src is the state before the transition. Src string // Dst is the state after the transition. Dst string // Err is an optional error that can be returned from a callback. Err error // Args is an optional list of arguments passed to the callback. Args []interface{} // contains filtered or unexported fields }
Event is the info that get passed as a reference in the callbacks.
type EventDesc ¶
type EventDesc struct { // Name is the event name used when calling for a transition. Name string // Src is a slice of source states that the FSM must be in to perform a // state transition. Src []string // Dst is the destination state that the FSM will be in if the transition // succeeds. Dst string }
EventDesc represents an event when initializing the FSM.
The event can have one or more source states that is valid for performing the transition. If the FSM is in one of the source states it will end up in the specified destination state, calling all defined callbacks as it goes.
type Events ¶
type Events []EventDesc
Events is a shorthand for defining the transition map in NewFSM.
type FSM ¶
type FSM struct {
// contains filtered or unexported fields
}
FSM is the state machine that holds the current state.
It has to be created with NewFSM to function properly.
func NewFSM ¶
NewFSM constructs a FSM from events and callbacks.
The events and transitions are specified as a slice of Event structs specified as Events. Each Event is mapped to one or more internal transitions from Event.Src to Event.Dst.
Callbacks are added as a map specified as Callbacks where the key is parsed as the callback event as follows, and called in the same order:
1. before_<EVENT> - called before event named <EVENT>
2. before_event - called before all events
3. leave_<OLD_STATE> - called before leaving <OLD_STATE>
4. leave_state - called before leaving all states
5. enter_<NEW_STATE> - called after entering <NEW_STATE>
6. enter_state - called after entering all states
7. after_<EVENT> - called after event named <EVENT>
8. after_event - called after all events
There are also two short form versions for the most commonly used callbacks. They are simply the name of the event or state:
1. <NEW_STATE> - called after entering <NEW_STATE>
2. <EVENT> - called after event named <EVENT>
If both a shorthand version and a full version is specified it is undefined which version of the callback will end up in the internal map. This is due to the pseudo random nature of Go maps. No checking for multiple keys is currently performed.
Example ¶
fsm := NewFSM( "green", Events{ {Name: "warn", Src: []string{"green"}, Dst: "yellow"}, {Name: "panic", Src: []string{"yellow"}, Dst: "red"}, {Name: "panic", Src: []string{"green"}, Dst: "red"}, {Name: "calm", Src: []string{"red"}, Dst: "yellow"}, {Name: "clear", Src: []string{"yellow"}, Dst: "green"}, }, Callbacks{ "before_warn": func(_ context.Context, e *Event) { fmt.Println("before_warn") }, "before_event": func(_ context.Context, e *Event) { fmt.Println("before_event") }, "leave_green": func(_ context.Context, e *Event) { fmt.Println("leave_green") }, "leave_state": func(_ context.Context, e *Event) { fmt.Println("leave_state") }, "enter_yellow": func(_ context.Context, e *Event) { fmt.Println("enter_yellow") }, "enter_state": func(_ context.Context, e *Event) { fmt.Println("enter_state") }, "after_warn": func(_ context.Context, e *Event) { fmt.Println("after_warn") }, "after_event": func(_ context.Context, e *Event) { fmt.Println("after_event") }, }, ) fmt.Println(fsm.Current()) err := fsm.Event(context.Background(), "warn") if err != nil { fmt.Println(err) } fmt.Println(fsm.Current())
Output: green before_warn before_event leave_green leave_state enter_yellow enter_state after_warn after_event yellow
func (*FSM) AvailableTransitions ¶
AvailableTransitions returns a list of transitions available in the current state.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, {Name: "kick", Src: []string{"closed"}, Dst: "broken"}, }, Callbacks{}, ) // sort the results ordering is consistent for the output checker transitions := fsm.AvailableTransitions() sort.Strings(transitions) fmt.Println(transitions)
Output: [kick open]
func (*FSM) Can ¶
Can returns true if event can occur in the current state.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, Callbacks{}, ) fmt.Println(fsm.Can("open")) fmt.Println(fsm.Can("close"))
Output: true false
func (*FSM) Cannot ¶
Cannot returns true if event can not occur in the current state. It is a convenience method to help code read nicely.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, Callbacks{}, ) fmt.Println(fsm.Cannot("open")) fmt.Println(fsm.Cannot("close"))
Output: false true
func (*FSM) Current ¶
Current returns the current state of the FSM.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, Callbacks{}, ) fmt.Println(fsm.Current())
Output: closed
func (*FSM) DeleteMetadata ¶ added in v1.0.0
DeleteMetadata deletes the dataValue in metadata by key
func (*FSM) Event ¶
Event initiates a state transition with the named event.
The call takes a variable number of arguments that will be passed to the callback, if defined.
It will return nil if the state change is ok or one of these errors:
- event X inappropriate because previous transition did not complete
- event X inappropriate in current state Y
- event X does not exist
- internal error on state transition
The last error should never occur in this situation and is a sign of an internal bug.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, Callbacks{}, ) fmt.Println(fsm.Current()) err := fsm.Event(context.Background(), "open") if err != nil { fmt.Println(err) } fmt.Println(fsm.Current()) err = fsm.Event(context.Background(), "close") if err != nil { fmt.Println(err) } fmt.Println(fsm.Current())
Output: closed open closed
func (*FSM) Is ¶
Is returns true if state is the current state.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, Callbacks{}, ) fmt.Println(fsm.Is("closed")) fmt.Println(fsm.Is("open"))
Output: true false
func (*FSM) SetMetadata ¶ added in v0.3.0
SetMetadata stores the dataValue in metadata indexing it with key
func (*FSM) SetState ¶
SetState allows the user to move to the given state from current state. The call does not trigger any callbacks, if defined.
func (*FSM) Transition ¶
Transition wraps transitioner.transition.
Example ¶
fsm := NewFSM( "closed", Events{ {Name: "open", Src: []string{"closed"}, Dst: "open"}, {Name: "close", Src: []string{"open"}, Dst: "closed"}, }, Callbacks{ "leave_closed": func(_ context.Context, e *Event) { e.Async() }, }, ) err := fsm.Event(context.Background(), "open") if e, ok := err.(AsyncError); !ok && e.Err != nil { fmt.Println(err) } fmt.Println(fsm.Current()) err = fsm.Transition() if err != nil { fmt.Println(err) } fmt.Println(fsm.Current())
Output: closed open
type InTransitionError ¶
type InTransitionError struct {
Event string
}
InTransitionError is returned by FSM.Event() when an asynchronous transition is already in progress.
func (InTransitionError) Error ¶
func (e InTransitionError) Error() string
type InternalError ¶
type InternalError struct{}
InternalError is returned by FSM.Event() and should never occur. It is a probably because of a bug.
func (InternalError) Error ¶
func (e InternalError) Error() string
type InvalidEventError ¶
InvalidEventError is returned by FSM.Event() when the event cannot be called in the current state.
func (InvalidEventError) Error ¶
func (e InvalidEventError) Error() string
type MermaidDiagramType ¶ added in v0.2.0
type MermaidDiagramType string
MermaidDiagramType the type of the mermaid diagram type
const ( // FlowChart the diagram type for output in flowchart style (https://mermaid-js.github.io/mermaid/#/flowchart) (including current state) FlowChart MermaidDiagramType = "flowChart" // StateDiagram the diagram type for output in stateDiagram style (https://mermaid-js.github.io/mermaid/#/stateDiagram) StateDiagram MermaidDiagramType = "stateDiagram" )
type NoTransitionError ¶
type NoTransitionError struct {
Err error
}
NoTransitionError is returned by FSM.Event() when no transition have happened, for example if the source and destination states are the same.
func (NoTransitionError) Error ¶
func (e NoTransitionError) Error() string
type NotInTransitionError ¶
type NotInTransitionError struct{}
NotInTransitionError is returned by FSM.Transition() when an asynchronous transition is not in progress.
func (NotInTransitionError) Error ¶
func (e NotInTransitionError) Error() string
type UnknownEventError ¶
type UnknownEventError struct {
Event string
}
UnknownEventError is returned by FSM.Event() when the event is not defined.
func (UnknownEventError) Error ¶
func (e UnknownEventError) Error() string
type VisualizeType ¶ added in v0.2.0
type VisualizeType string
VisualizeType the type of the visualization
const ( // GRAPHVIZ the type for graphviz output (http://www.webgraphviz.com/) GRAPHVIZ VisualizeType = "graphviz" // MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form MERMAID VisualizeType = "mermaid" // MermaidStateDiagram the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form MermaidStateDiagram VisualizeType = "mermaid-state-diagram" // MermaidFlowChart the type for mermaid output (https://mermaid-js.github.io/mermaid/#/flowchart) in the flow chart form MermaidFlowChart VisualizeType = "mermaid-flow-chart" )