Typed finite-state machine (FSM) generator for Go.
It generates state types, transition helpers, and transition interfaces so illegal transitions fail at compile time.
- No runtime surprises - compile-time checked transitions via typed state unions (similar to sum types).
- Easy to change – update the FSM config, generate, and fix compiler errors.
- Machine structure is defined in
protobuftext format. fsm-gen-fsmreads the machine definition and generates strongly typed Go code.- Transition implementations are regular Go functions provided by your app.
Schema: proto/fsm/v1/fsm.proto
Example machine: examples/simple/fsm.textproto
initial_state: "open"
states: { name: "open" next: "read" next: "write" }
states: { name: "read" next: "close" }
states: { name: "write" next: "close" }
states: { name: "close" }
type Data struct {
count int
}
data := &Data{}
transitions := Transitions[*Data]{
OnOpen: func(_ context.Context, d *Data, s Open) (ReadWrite, error) {
d.count += 1
return s.ToRead(), nil
},
OnRead: func(_ context.Context, d *Data, _ Read) (Close, error) {
d.count += 1
return Close{}, nil
},
OnWrite: func(_ context.Context, d *Data, _ Write) (Close, error) {
return Close{}, nil
},
OnClose: func(_ context.Context, d *Data, _ Close) error {
d.count += 1
return nil
},
}
fsm, err := CreateFSM(data, transitions)
if err != nil {
t.Fatalf("CreateFSM: %v", err)
}
final, err := fsm.Run(context.Background())
if err != nil {
t.Fatalf("Run: %v", err)
}
if _, ok := final.(Close); !ok {
t.Fatalf("expected Close, got %T", final)
}protocandprotoc-gen-gofor protobuf generation
- Generate protobuf types:
go generate ./proto/fsm/v1- Generate FSM code from a machine spec:
go run ./cmd/fsm-gen-fsm -in examples/simple/fsm.textproto -out examples/simple/fsm_gen.go -pkg simple- Run tests:
go test ./...You can also generate the example package directly:
cd examples/simple
go generate ./...