Skip to content

Commit

Permalink
feat(fsm): support fallback transitions
Browse files Browse the repository at this point in the history
support a transition that applies to any source state
  • Loading branch information
hannahhoward committed Feb 19, 2020
1 parent deaf473 commit c670342
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 2 deletions.
10 changes: 8 additions & 2 deletions fsm/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ func NewFSMHandler(world World, state StateType, stateKeyField StateKeyField, ev
if dst != nil {
d.stateHandlers[dst] = nil
}
if !reflect.TypeOf(src).AssignableTo(stateFieldType.Type) {
if src != nil && !reflect.TypeOf(src).AssignableTo(stateFieldType.Type) {
return nil, xerrors.Errorf("event `%s` source type is not assignable to: %s", name, stateFieldType.Type.Name())
}
d.transitions[eKey{name, src}] = dst
d.stateHandlers[src] = nil
if src != nil {
d.stateHandlers[src] = nil
}
}
}

Expand Down Expand Up @@ -131,6 +133,10 @@ func (d fsmHandler) Plan(events []statemachine.Event, user interface{}) (interfa
currentState := userValue.Elem().FieldByName(string(d.stateKeyField)).Interface()
e := events[0].User.(fsmEvent)
destination, ok := d.transitions[eKey{e.name, currentState}]
// check for fallback transition for any source state
if !ok {
destination, ok = d.transitions[eKey{e.name, nil}]
}
if !ok {
return d.completePlan(e, nil, 1, xerrors.Errorf("Invalid transition in queue, state `%+v`, event `%s`", currentState, e.name))
}
Expand Down
25 changes: 25 additions & 0 deletions fsm/fsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ var events = fsm.Events{
uint64(2): nil,
},
},
"any": {
TransitionMap: fsm.TransitionMap{
nil: uint64(1),
},
},
}

var stateHandlers = fsm.StateHandlers{
Expand Down Expand Up @@ -305,3 +310,23 @@ func TestNoChangeHandler(t *testing.T) {
require.NoError(t, err)
<-tw.done
}

func TestAllStateEvent(t *testing.T) {
ds := datastore.NewMapDatastore()

tw := &testWorld{t: t, done: make(chan struct{}), proceed: make(chan struct{}), universalCalls: 0}
close(tw.proceed)
smm, err := fsm.New(ds, tw, statemachine.TestState{}, "A", events, stateHandlers)
require.NoError(t, err)

// any can run from any state and function like start
err = smm.Send(uint64(2), "any")
require.NoError(t, err)
<-tw.done

tw.done = make(chan struct{})
// here any can function like a restart handler
err = smm.Send(uint64(2), "any")
require.NoError(t, err)
<-tw.done
}
1 change: 1 addition & 0 deletions fsm/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type StateKeyField string
// is designated the state key and must be comparable
type StateKey interface{}

// TransitionMap is a map from src state to destination state
type TransitionMap map[StateKey]StateKey

// EventDesc describes what happens when an event is triggered
Expand Down

0 comments on commit c670342

Please sign in to comment.