Finite State Machine (or FSM) written in Go. It is a low-level primitive for more complex solutions. Supported (optional) transition handler for advanced logic.
What is a finite state machine (or FSM)? TODO
- use as simple FSM or with transition handlers for advanced logic
- dispatcher is thread-safe
- implemented
prometheus.Collector
for monitoring
Install or update ffsm
package.
go get -u github.com/gebv/ffsm
Simple example:
// enum of states
var (
OpenDoor = "open"
CloseDoor = "close"
)
// setup of state transition diagram
wf := make(ffsm.Stack).Add(OpenDoor, CloseDoor).Add(CloseDoor, OpenDoor)
// init FSM with initial state CloseDoor
fsm := ffsm.NewFSM(wf, CloseDoor)
// to opens the door
err := fsm.Dispatch(context.Background(), OpenDoor)
if err != nil {
// handler errors
}
fsm.State() // end state
Finite state machine example
Can open door and close door. Opened door can not open. And closed door can not close.
Follow state transition diagram in image
On the playground https://play.golang.org/p/J8Cej99Rp-J
Listing code
package main
import (
"context"
"fmt"
"github.com/gebv/ffsm"
)
func main() {
// setup state transition diagram
wf := make(ffsm.Stack).Add(OpenDoor, CloseDoor).Add(CloseDoor, OpenDoor)
// init FSM with initial state CloseDoor
fsm := ffsm.NewFSM(wf, CloseDoor)
fmt.Println("initial state:", fsm.State())
fmt.Println()
fmt.Println("to open door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
err := fsm.Dispatch(context.Background(), OpenDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
fmt.Println()
fmt.Println("to open door for opened door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
err = fsm.Dispatch(context.Background(), OpenDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
fmt.Println()
fmt.Println("to close door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
err = fsm.Dispatch(context.Background(), CloseDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
}
const (
OpenDoor = "open"
CloseDoor = "close"
)
Only Bob can open door. Anyone can close the door. And also opened door can not open and closed door can not close.
Follow state transition diagram in image
On the playground https://play.golang.org/p/M5JiBwUycnx
Listing code
package main
import (
"context"
"fmt"
"errors"
"github.com/gebv/ffsm"
)
func main() {
// handler for CloseDoor to OpenDoor transition
onlyBobHandler := func(ctx context.Context) (context.Context, error) {
name, ok := ctx.Value("__name").(string)
if !ok {
return ctx, errors.New("forbidden - only for Bob")
}
if name != "bob" {
return ctx, errors.New("forbidden - only for Bob")
}
return ctx, nil
}
// setup state transition diagram
wf := make(ffsm.Stack).Add(OpenDoor, CloseDoor).Add(CloseDoor, OpenDoor, onlyBobHandler)
// init FSM with initial state CloseDoor
fsm := ffsm.NewFSM(wf, CloseDoor)
fmt.Println("initial state:", fsm.State())
fmt.Println()
fmt.Println("anonymous opens door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
err := fsm.Dispatch(context.Background(), OpenDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
fmt.Println()
fmt.Println("Bob opens door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
bobCtx := context.WithValue(context.Background(), "__name", "bob")
err = fsm.Dispatch(bobCtx, OpenDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
fmt.Println()
fmt.Println("to open door for opened door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
err = fsm.Dispatch(context.Background(), OpenDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
fmt.Println()
fmt.Println("to close door")
fmt.Println("----------------------------")
fmt.Println("before:", fsm.State())
err = fsm.Dispatch(context.Background(), CloseDoor)
if err != nil {
fmt.Println("failed:", err)
}
fmt.Println("after:", fsm.State())
}
const (
OpenDoor = "open"
CloseDoor = "close"
)
- github.com/prometheus/client_golang prometheus client for golang
- github.com/stretchr/testify helper package for testing
ffsm
follows semantic versioning for the documented public API on stable releases. v1.2 is the latest stable version. Follows changelog.
MIT, see LICENSE.