Skip to content

Commit

Permalink
feat: return last and current state on transition
Browse files Browse the repository at this point in the history
  • Loading branch information
yannickkirschen committed Nov 22, 2024
1 parent e17ea26 commit 64f4ede
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 16 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Basic usage works as follows:
Here is an example code based on the very simple use-case of a door:

```go
// Signature: <initial state>
// Signature: <machine> = <initial state>
machine := fsm.NewMachine("close")

// Signature: <current state, input event, next state>
Expand All @@ -40,8 +40,8 @@ machine.SetExitAction(func(current, next string) error {

fmt.Printf("Current state is %s\n", machine.State())

// Signature: <input event>
if err := machine.Transition("open-door"); err != nil {
// Signature: <last, current, err> = <input event>
if _, _, err := machine.Transition("open-door"); err != nil {
panic(err)
}

Expand Down
6 changes: 3 additions & 3 deletions examples/door-complex-types/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ func main() {
return nil
})

machine.SetExitAction(func(old, next *DoorState) error {
fmt.Printf("Leaving '%s' going to '%s'\n", old.Id, next.Id)
machine.SetExitAction(func(current, next *DoorState) error {
fmt.Printf("Leaving '%s' going to '%s'\n", current.Id, next.Id)
return nil
})

fmt.Printf("Current state is %+v\n", machine.State())

if err := machine.Transition("open-door"); err != nil {
if _, _, err := machine.Transition("open-door"); err != nil {
panic(err)
}

Expand Down
2 changes: 1 addition & 1 deletion examples/door/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func main() {

fmt.Printf("Current state is %s\n", machine.State())

if err := machine.Transition("open-door"); err != nil {
if _, _, err := machine.Transition("open-door"); err != nil {
panic(err)
}

Expand Down
15 changes: 10 additions & 5 deletions state-machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,30 @@ func (machine *Machine[T]) SetExitAction(f StateActionFunc[T]) {
// possible, an error is returned.
// When leaving the old state, the exit action is called (if provided). When
// entering the new state, the enter action is called (if provided).
func (machine *Machine[T]) Transition(input Event) error {
func (machine *Machine[T]) Transition(input Event) (*T, *T, error) {
transition := machine.findTransition(machine.current, input)
if transition == nil {
return fmt.Errorf("there is no state to transition to from state '%+v' on event '%s'", machine.current, input)
return nil, nil, fmt.Errorf("there is no state to transition to from state '%+v' on event '%s'", machine.current, input)
}

next := transition.Next

if machine.exitAction != nil {
if err := machine.exitAction(machine.current, next); err != nil {
return err
return nil, nil, err
}
}

if machine.enterAction != nil {
if err := machine.enterAction(machine.current, next); err != nil {
return err
return nil, nil, err
}
}

// We will only enter the next state if the enter action was successful.
last := copy(machine.current)
machine.current = next
return nil
return &last, &machine.current, nil
}

// CanTransition checks if the machine can transition to a new state when the
Expand All @@ -117,3 +118,7 @@ func (machine *Machine[T]) findTransition(current T, input Event) *Transition[T]

return nil
}

func copy[T any](t T) T {
return t
}
22 changes: 18 additions & 4 deletions state-machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ func TestState(t *testing.T) {
func TestTransition(t *testing.T) {
machine := getMachine()

if err := machine.Transition(OpenDoor); err != nil {
if _, _, err := machine.Transition(OpenDoor); err != nil {
t.Error(err)
}

if err := machine.Transition(OpenDoor); err == nil {
if _, _, err := machine.Transition(OpenDoor); err == nil {
t.Error("expecting door to be opened")
}
}
Expand All @@ -82,16 +82,30 @@ func TestCanTransition(t *testing.T) {
func TestComplexTransition(t *testing.T) {
machine := getComplexMachine()

if err := machine.Transition(OpenDoor); err != nil {
var last *ComplexDoorState
var current *ComplexDoorState
if l, c, err := machine.Transition(OpenDoor); err != nil {
t.Error(err)
} else {
last = *l
current = *c
}

if err := machine.Transition(OpenDoor); err == nil {
if _, _, err := machine.Transition(OpenDoor); err == nil {
t.Error("expecting door to be opened")
}

state := machine.State()
if state.OpenedBy != "Peter" {
t.Errorf("expecting state %s to be 'Peter', not %s", state.Id, state.OpenedBy)
}

if state.Id != current.Id {
t.Errorf("States are not equal (%s vs %s)", state.Id, current.Id)
}

last.Id = "this should have no effect"
if state.Id == last.Id {
t.Errorf("State and last state are equal")
}
}

0 comments on commit 64f4ede

Please sign in to comment.