Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for metadata #63

Merged
merged 8 commits into from
Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,11 @@ type InternalError struct{}
func (e InternalError) Error() string {
return "internal error on state transition"
}

// NoDataError is returned by FSM.ReadData() when the data being queried isn't
// present
type NoDataError struct{}

func (e NoDataError) Error() string {
return "Specified Data not available"
}
46 changes: 46 additions & 0 deletions examples/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// +build ignore

package main

import (
"fmt"

"github.com/looplab/fsm"
)

func main() {
fsm := fsm.NewFSM(
"idle",
fsm.Events{
{Name: "produce", Src: []string{"idle"}, Dst: "idle"},
{Name: "consume", Src: []string{"idle"}, Dst: "idle"},
},
fsm.Callbacks{
"produce": func(e *fsm.Event) {
e.FSM.SetMetadata("message", "hii")
fmt.Println("produced data")
},
"consume": func(e *fsm.Event) {
message := e.FSM.Metadata("message").(string)
fmt.Println("message = " + message)
},
},
)

fmt.Println(fsm.Current())

err := fsm.Event("produce")
if err != nil {
fmt.Println(err)
}

fmt.Println(fsm.Current())

err = fsm.Event("consume")
if err != nil {
fmt.Println(err)
}

fmt.Println(fsm.Current())

}
44 changes: 42 additions & 2 deletions fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type FSM struct {
// transitions maps events and source states to destination states.
transitions map[eKey]string

// callbacks maps events and targers to callback functions.
// callbacks maps events and targets to callback functions.
callbacks map[cKey]Callback

// transition is the internal transition functions used either directly
Expand All @@ -57,6 +57,10 @@ type FSM struct {
stateMu sync.RWMutex
// eventMu guards access to Event() and Transition().
eventMu sync.Mutex
// data can be used to store and load data that maybe used across events
metadata map[string]*DataValue

metadataMu sync.Mutex
}

// EventDesc represents an event when initializing the FSM.
Expand Down Expand Up @@ -87,6 +91,15 @@ type Events []EventDesc
// Callbacks is a shorthand for defining the callbacks in NewFSM.
type Callbacks map[string]Callback

// DataValue is stored in data which can be indexed using key. It has Value to
// store underlying data and ValueMu used for safely storing and retrieving
type DataValue struct {
maxekman marked this conversation as resolved.
Show resolved Hide resolved
// valueMu guards access to the value underlying
ValueMu sync.RWMutex
// value has the actual data which may be used store data at the machine level
Value interface{}
}

// NewFSM constructs a FSM from events and callbacks.
//
// The events and transitions are specified as a slice of Event structs
Expand Down Expand Up @@ -129,6 +142,7 @@ func NewFSM(initial string, events []EventDesc, callbacks map[string]Callback) *
current: initial,
transitions: make(map[eKey]string),
callbacks: make(map[cKey]Callback),
metadata: make(map[string]*DataValue),
}

// Build transition map and store sets of all events and states.
Expand Down Expand Up @@ -229,7 +243,7 @@ func (f *FSM) Can(event string) bool {
return ok && (f.transition == nil)
}

// AvailableTransitions returns a list of transitions avilable in the
// AvailableTransitions returns a list of transitions available in the
// current state.
func (f *FSM) AvailableTransitions() []string {
f.stateMu.RLock()
Expand All @@ -249,6 +263,32 @@ func (f *FSM) Cannot(event string) bool {
return !f.Can(event)
}

// Metadata returns the value stored in data
func (f *FSM) Metadata(key string) interface{} {
dataElement, ok := f.metadata[key]
maxekman marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
return NoDataError{}
}
dataElement.ValueMu.RLock()
defer dataElement.ValueMu.RUnlock()
return dataElement.Value
}

// SetMetadata stores the dataValue in data indexing it with key
func (f *FSM) SetMetadata(key string, dataValue interface{}) {
dataElement, ok := f.metadata[key]
maxekman marked this conversation as resolved.
Show resolved Hide resolved
if ok {
dataElement.ValueMu.Lock()
dataElement.Value = dataValue
dataElement.ValueMu.Unlock()
} else {
f.metadataMu.Lock()
f.metadata[key] = &DataValue{Value: dataValue}
f.metadataMu.Unlock()
}

}

// Event initiates a state transition with the named event.
//
// The call takes a variable number of arguments that will be passed to the
Expand Down
4 changes: 2 additions & 2 deletions visualizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const (
GRAPHVIZ VisualizeType = "graphviz"
// MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form
MERMAID VisualizeType = "mermaid"
// MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form
// MermaidStateDiagram the type for mermaid output (https://mermaid-js.github.io/mermaid/#/stateDiagram) in the stateDiagram form
MermaidStateDiagram VisualizeType = "mermaid-state-diagram"
// MERMAID the type for mermaid output (https://mermaid-js.github.io/mermaid/#/flowchart) in the flow chart form
// MermaidFlowChart the type for mermaid output (https://mermaid-js.github.io/mermaid/#/flowchart) in the flow chart form
MermaidFlowChart VisualizeType = "mermaid-flow-chart"
)

Expand Down