summaryrefslogtreecommitdiff
path: root/internal/fsm/fsm.go
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2022-11-28 11:18:14 +0100
committerjwijenbergh <jeroenwijenbergh@protonmail.com>2022-11-28 11:18:42 +0100
commite9f8db8ee8fccf60e58deb1d72766f94a053bb16 (patch)
treeffa5a9be67717ecc8ff7bdc03d5f96028facb0e3 /internal/fsm/fsm.go
parentb4ff890ec2b459148d893499a34a6d2954530369 (diff)
Document: Add comments for most functions and packages
Errors and test files still need to be done. Also some getters are changed by removing the 'get' prefix
Diffstat (limited to 'internal/fsm/fsm.go')
-rw-r--r--internal/fsm/fsm.go71
1 files changed, 50 insertions, 21 deletions
diff --git a/internal/fsm/fsm.go b/internal/fsm/fsm.go
index b51a5c9..b8fd644 100644
--- a/internal/fsm/fsm.go
+++ b/internal/fsm/fsm.go
@@ -1,3 +1,5 @@
+// Package fsm defines a finite state machine and has the ability to save this state machine to a graph file
+// This graph file can be visualized using mermaid.js
package fsm
import (
@@ -10,7 +12,9 @@ import (
)
type (
+ //StateID represents the Identifier of the state
FSMStateID int8
+ //StateIDSlice represents the list of state identifiers
FSMStateIDSlice []FSMStateID
)
@@ -26,8 +30,11 @@ func (v FSMStateIDSlice) Swap(i, j int) {
v[i], v[j] = v[j], v[i]
}
+// Transition indicates an arrow in the state graph
type FSMTransition struct {
+ // To represents the to-be-new state
To FSMStateID
+ // Description is what type of message the arrow gets in the graph
Description string
}
@@ -35,45 +42,60 @@ type (
FSMStates map[FSMStateID]FSMState
)
+// State represents a single node in the graph
type FSMState struct {
+ // Transitions indicates which out arrows this node has
Transitions []FSMTransition
-
- // Which state to go back to on a back transition
- BackState FSMStateID
}
+// FSM represents the total graph
type FSM struct {
+ // States is the map from state ID to states
States FSMStates
+
+ // Current is the current state represented by the identifier
Current FSMStateID
- // Info to be passed from the parent state
+ // Name represents the descriptive name of this state machine
Name string
+
+ // StateCallback is the function ran when a transition occurs
+ // It takes the old state, the new state and the data and returns if this is handled by the client
StateCallback func(FSMStateID, FSMStateID, interface{}) bool
+
+ // Directory represents the path where the state graph is stored
Directory string
- Debug bool
- GetName func(FSMStateID) string
+
+ // Generate represents whether we want to generate the graph
+ Generate bool
+
+ // GetStateName gets the name of a state as a string
+ GetStateName func(FSMStateID) string
}
+// Init initializes the state machine and sets it to the given current state
func (fsm *FSM) Init(
current FSMStateID,
states map[FSMStateID]FSMState,
callback func(FSMStateID, FSMStateID, interface{}) bool,
directory string,
nameGen func(FSMStateID) string,
- debug bool,
+ generate bool,
) {
fsm.States = states
fsm.Current = current
fsm.StateCallback = callback
fsm.Directory = directory
- fsm.GetName = nameGen
- fsm.Debug = debug
+ fsm.GetStateName = nameGen
+ fsm.Generate = generate
}
+// InState returns whether or not the state machine is in the given 'check' state
func (fsm *FSM) InState(check FSMStateID) bool {
return check == fsm.Current
}
+// HasTransition checks whether or not the state machine has a transition to the given 'check' state
func (fsm *FSM) HasTransition(check FSMStateID) bool {
for _, transitionState := range fsm.States[fsm.Current].Transitions {
if transitionState.To == check {
@@ -84,11 +106,13 @@ func (fsm *FSM) HasTransition(check FSMStateID) bool {
return false
}
+// getGraphFilename gets the full path to the graph filename including the .graph extension
func (fsm *FSM) getGraphFilename(extension string) string {
debugPath := path.Join(fsm.Directory, "graph")
return fmt.Sprintf("%s%s", debugPath, extension)
}
+// writeGraph writes the state machine to a .graph file
func (fsm *FSM) writeGraph() {
graph := fsm.GenerateGraph()
graphFile := fsm.getGraphFilename(".graph")
@@ -107,18 +131,18 @@ func (fsm *FSM) writeGraph() {
}
}
-func (fsm *FSM) GoBack() {
- fsm.GoTransition(fsm.States[fsm.Current].BackState)
-}
-
+// GoTransitionRequired transitions the state machine to a new state with associated state data 'data'
+// If this transition is not handled by the client, it returns an error
func (fsm *FSM) GoTransitionRequired(newState FSMStateID, data interface{}) error {
oldState := fsm.Current
if !fsm.GoTransitionWithData(newState, data) {
- return types.NewWrappedError("failed required transition", fmt.Errorf("required transition not handled, from: %s -> to: %s", fsm.GetName(oldState), fsm.GetName(newState)))
+ return types.NewWrappedError("failed required transition", fmt.Errorf("required transition not handled, from: %s -> to: %s", fsm.GetStateName(oldState), fsm.GetStateName(newState)))
}
return nil
}
+// GoTransitionWithData is a helper that transitions the state machine toward the 'newState' with associated state data 'data'
+// It returns whether or not the transition is handled by the client
func (fsm *FSM) GoTransitionWithData(newState FSMStateID, data interface{}) bool {
ok := fsm.HasTransition(newState)
@@ -126,7 +150,7 @@ func (fsm *FSM) GoTransitionWithData(newState FSMStateID, data interface{}) bool
if ok {
oldState := fsm.Current
fsm.Current = newState
- if fsm.Debug {
+ if fsm.Generate {
fsm.writeGraph()
}
@@ -136,11 +160,14 @@ func (fsm *FSM) GoTransitionWithData(newState FSMStateID, data interface{}) bool
return handled
}
+// GoTransition is an alias to call GoTransitionWithData but have an empty string as data
func (fsm *FSM) GoTransition(newState FSMStateID) bool {
// No data means the callback is never required
return fsm.GoTransitionWithData(newState, "")
}
+// generateMermaidGraph generates a graph suitable to be converted by the mermaid.js tool
+// it returns the graph as a string
func (fsm *FSM) generateMermaidGraph() string {
graph := "graph TD\n"
sortedFSM := make(FSMStateIDSlice, 0, len(fsm.States))
@@ -152,15 +179,15 @@ func (fsm *FSM) generateMermaidGraph() string {
transitions := fsm.States[state].Transitions
for _, transition := range transitions {
if state == fsm.Current {
- graph += "\nstyle " + fsm.GetName(state) + " fill:cyan\n"
+ graph += "\nstyle " + fsm.GetStateName(state) + " fill:cyan\n"
} else {
- graph += "\nstyle " + fsm.GetName(state) + " fill:white\n"
+ graph += "\nstyle " + fsm.GetStateName(state) + " fill:white\n"
}
- graph += fsm.GetName(
+ graph += fsm.GetStateName(
state,
- ) + "(" + fsm.GetName(
+ ) + "(" + fsm.GetStateName(
state,
- ) + ") " + "-->|" + transition.Description + "| " + fsm.GetName(
+ ) + ") " + "-->|" + transition.Description + "| " + fsm.GetStateName(
transition.To,
) + "\n"
}
@@ -168,8 +195,10 @@ func (fsm *FSM) generateMermaidGraph() string {
return graph
}
+// GenerateGraph generates a mermaid graph if the state machine is initialized
+// If the graph cannot be generated, it returns the empty string
func (fsm *FSM) GenerateGraph() string {
- if fsm.GetName != nil {
+ if fsm.GetStateName != nil {
return fsm.generateMermaidGraph()
}