Skip to content

Commit

Permalink
fix: add Diagram and avail source state in the event
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkgos committed Jul 1, 2023
1 parent decf899 commit d1f3f34
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 16 deletions.
9 changes: 9 additions & 0 deletions fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import (
"golang.org/x/exp/constraints"
)

type Diagram interface {
// Visualize outputs a visualization of a Fsm in the desired format.
// If the type is not given it defaults to Graphviz
Visualize(t VisualizeType) (string, error)
}

type IFsm[E constraints.Ordered, S constraints.Ordered] interface {
// Clone the Fsm.
Clone() IFsm[E, S]
Expand Down Expand Up @@ -50,6 +56,8 @@ type IFsm[E constraints.Ordered, S constraints.Ordered] interface {
ContainsAllEvent(events ...E) bool
// AvailEvents returns a list of available transform event in src state.
AvailEvents(srcState S) []E
// AvailSourceStates returns a list of available source state in the event.
AvailSourceStates(event E) []S
// SortedTriggerSource return a list of sorted trigger source
SortedTriggerSource() []TriggerSource[E, S]
// SortedStates return a list of sorted states.
Expand All @@ -60,6 +68,7 @@ type IFsm[E constraints.Ordered, S constraints.Ordered] interface {
EventName(event E) string
// StateName returns a state name.
StateName(state S) string
Diagram
}

type ErrorTranslator interface {
Expand Down
3 changes: 3 additions & 0 deletions fsm_safe.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,6 @@ func (f *SafeFsm[E, S]) MatchCurrentAllOccur(event ...E) bool {
func (f *SafeFsm[E, S]) CurrentAvailEvents() []E {
return f.Transition.AvailEvents(f.current)
}
func (f *SafeFsm[E, S]) Visualize(t VisualizeType) (string, error) {
return Visualize[E, S](t, f)
}
12 changes: 8 additions & 4 deletions fsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ func test_Fsm_State(t *testing.T, newFsm func(initState string, ts *Transition[s
}
}

func Test_Fsm_AvailEvents(t *testing.T) {
test_Fsm_AvailEvents(t, NewSafeFsm[string, string])
test_Fsm_AvailEvents(t, NewFsm[string, string])
func Test_Fsm_Avail(t *testing.T) {
test_Fsm_Avail(t, NewSafeFsm[string, string])
test_Fsm_Avail(t, NewFsm[string, string])
}

func test_Fsm_AvailEvents(t *testing.T, newFsm func(initState string, ts *Transition[string, string]) IFsm[string, string]) {
func test_Fsm_Avail(t *testing.T, newFsm func(initState string, ts *Transition[string, string]) IFsm[string, string]) {
fsm := newFsm(
"closed",
NewTransition([]Transform[string, string]{
Expand All @@ -133,6 +133,10 @@ func test_Fsm_AvailEvents(t *testing.T, newFsm func(initState string, ts *Transi
if !slices.Equal(sortedEvents, []string{"close", "middle", "open"}) {
t.Error("expected sort event [close, middle, open] event with current state")
}
availSourceStates := fsm.AvailSourceStates("open")
if !slices.Contains(availSourceStates, "closed") {
t.Error("expected avail source state [closed] with the event")
}

if !fsm.MatchCurrentAllOccur("middle", "open") {
t.Error("expected contain all [middle, open] event with current state")
Expand Down
19 changes: 15 additions & 4 deletions fsm_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ var (
ErrNonExistEvent = errors.New("fsm: event does not exist")
)

// Transform represents an event when initializing the FSM.
// Transform 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 transition. If the Fsm is in one of the source states it will end up in
// the specified destination state.
type Transform[E constraints.Ordered, S constraints.Ordered] struct {
// Name the event.
Name string
// Event is the event used when calling for the transform.
Event E
// Src is a slice of source states that the FSM must be in to perform a
// Src is a slice of source states that the Fsm must be in to perform a
// state transform.
Src []S
// Dst is the destination state that the FSM will be in if the transform
// Dst is the destination state that the Fsm will be in if the transform
// succeeds.
Dst S
}
Expand Down Expand Up @@ -192,6 +192,17 @@ func (t *Transition[E, S]) AvailEvents(srcState S) []E {
return maps.Keys(events)
}

// AvailSourceStates returns a list of available source state in the event.
func (t *Transition[E, S]) AvailSourceStates(event E) []S {
srcs := make([]S, 0, 8)
for ts := range t.mapping {
if ts.event == event {
srcs = append(srcs, ts.src)
}
}
return srcs
}

// SortedTriggerSource return a list of sorted trigger source
func (t *Transition[E, S]) SortedTriggerSource() []TriggerSource[E, S] {
triggerSources := maps.Keys(t.mapping)
Expand Down
3 changes: 3 additions & 0 deletions fsm_unsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ func (f *Fsm[E, S]) MatchCurrentAllOccur(event ...E) bool {
func (f *Fsm[E, S]) CurrentAvailEvents() []E {
return f.Transition.AvailEvents(f.current)
}
func (f *Fsm[E, S]) Visualize(t VisualizeType) (string, error) {
return Visualize[E, S](t, f)
}
4 changes: 2 additions & 2 deletions visualizer._mermaid.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
StateDiagram MermaidType = "stateDiagram"
)

// VisualizeMermaid outputs a visualization of a FSM in Mermaid format as specified by the graphType.
// VisualizeMermaid outputs a visualization of a Fsm in Mermaid format as specified by the graphType.
func VisualizeMermaid[E constraints.Ordered, S constraints.Ordered](t MermaidType, fsm Visualizer[E, S]) (string, error) {
switch t {
case FlowChart:
Expand Down Expand Up @@ -52,7 +52,7 @@ func visualizeMermaidStateDiagram[E constraints.Ordered, S constraints.Ordered](
return buf.String(), nil
}

// visualizeMermaidFlowChart outputs a visualization of a FSM in Mermaid format (including highlighting of current state).
// visualizeMermaidFlowChart outputs a visualization of a Fsm in Mermaid format (including highlighting of current state).
func visualizeMermaidFlowChart[E constraints.Ordered, S constraints.Ordered](fsm Visualizer[E, S]) (string, error) {
v := newVisualizeMermaidFlowChartBuilder(fsm).
writeFlowChartGraphType().
Expand Down
4 changes: 2 additions & 2 deletions visualizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"golang.org/x/exp/constraints"
)

// Visualize outputs a visualization of a FSM in the desired format.
// Visualize outputs a visualization of a Fsm in the desired format.
type Visualizer[E constraints.Ordered, S constraints.Ordered] interface {
Current() S
Name() string
Expand All @@ -30,7 +30,7 @@ const (
MermaidFlowChart VisualizeType = "mermaid-flow-chart"
)

// Visualize outputs a visualization of a FSM in the desired format.
// Visualize outputs a visualization of a Fsm in the desired format.
// If the type is not given it defaults to Graphviz
func Visualize[E constraints.Ordered, S constraints.Ordered](t VisualizeType, fsm Visualizer[E, S]) (string, error) {
switch t {
Expand Down
2 changes: 1 addition & 1 deletion visualizer_graphviz.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"golang.org/x/exp/constraints"
)

// VisualizeGraphviz outputs a visualization of a FSM in Graphviz format.
// VisualizeGraphviz outputs a visualization of a Fsm in Graphviz format.
func VisualizeGraphviz[E constraints.Ordered, S constraints.Ordered](fsm Visualizer[E, S]) (string, error) {
v := newVisualizeGraphvizBuilder(fsm).
writeHeaderLine().
Expand Down
7 changes: 4 additions & 3 deletions visualizer_graphviz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (
)

func Test_Graphviz(t *testing.T) {
fsmUnderTest := NewSafeFsm(
fsmUnderTest := NewFsm(
"closed",
NewTransition([]Transform[string, string]{
{Event: "open", Src: []string{"closed"}, Dst: "open"},
{Event: "close", Src: []string{"open"}, Dst: "closed"},
{Event: "part-close", Src: []string{"intermediate"}, Dst: "closed"},
}),
)
got, err := VisualizeGraphviz[string, string](fsmUnderTest)
got, err := fsmUnderTest.Visualize(Graphviz)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -54,7 +54,8 @@ func Test_Graphviz_CustomName(t *testing.T) {
}).
Build(),
)
got, err := VisualizeGraphviz[string, string](fsmUnderTest)

got, err := fsmUnderTest.Visualize(Graphviz)
if err != nil {
panic(err)
}
Expand Down

0 comments on commit d1f3f34

Please sign in to comment.