https://github.com/bykof/stateful
Finite state machine for Go
https://github.com/bykof/stateful
go golang graph state-machine transitions
Last synced: 6 months ago
JSON representation
Finite state machine for Go
- Host: GitHub
- URL: https://github.com/bykof/stateful
- Owner: bykof
- License: mit
- Created: 2019-07-18T18:51:26.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2020-05-24T08:37:10.000Z (over 5 years ago)
- Last Synced: 2024-08-04T01:23:47.023Z (about 1 year ago)
- Topics: go, golang, graph, state-machine, transitions
- Language: Go
- Size: 647 KB
- Stars: 209
- Watchers: 14
- Forks: 8
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-list - stateful
README
Welcome to stateful 👋
![]()
> Create easy state machines with your existing code
# Table of Contents
1. [Documentation](#documentation)
2. [Usage](#usage)
3. [Draw graph](#draw-graph)
4. [Wildcards](#wildcards)
5. [Examples](#examples)
6. [Credits](#credits)## Documentation
You can find the documentation here: [https://pkg.go.dev/github.com/bykof/stateful?tab=doc](https://pkg.go.dev/github.com/bykof/stateful?tab=doc)
## Usage
It is very easy to use stateful.
Just create a struct and implement the `stateful` interface
```go
import "github.com/bykof/stateful"type (
MyMachine struct {
state stateful.State
amount int
}
AmountParams struct {
Amount int
}
)func NewMyMachine() MyMachine {
return MyMachine{
state: A,
amount: 0,
}
}// State implement interface stateful
func (mm MyMachine) State() stateful.State {
return mm.state
}// SetState implement interface stateful
func (mm *MyMachine) SetState(state stateful.State) error {
mm.state = state
return nil
}
```Declare some proper states:
```go
const (
A = stateful.DefaultState("A")
B = stateful.DefaultState("B")
)
```Then add some transitions to the machine:
```go
// Declare a transition of you machine and return the new state of the machine.
func (mm *MyMachine) FromAToB(transitionArguments stateful.TransitionArguments) (stateful.State, error) {
amountParams, ok := transitionArguments.(AmountParams)
if !ok {
return nil, errors.New("could not parse AmountParams")
}
mm.amount += amountParams.Amount
return B, nil
}func (mm *MyMachine) FromBToA(transitionArguments stateful.TransitionArguments) (stateful.State, error) {
amountParams, ok := transitionArguments.(AmountParams)
if !ok {
return nil, errors.New("could not parse AmountParams")
}
mm.amount -= amountParams.Amount
return A, nil
}// The state machine will check, if you transfer to a proper and defined state in the machine. See below.
func (mm *MyMachine) FromAToNotExistingC(_ stateful.TransitionArguments) (stateful.State, error) {
return stateful.DefaultState("C")
}
```And now initialize the machine:
```go
myMachine := NewMyMachine()
stateMachine := &stateful.StateMachine{
StatefulObject: &myMachine,
}stateMachine.AddTransition(
// The transition function
myMachine.FromAToB,
// SourceStates
stateful.States{A},
// DestinationStates
stateful.States{B},
)stateMachine.AddTransition(
myMachine.FromBToA,
stateful.States{B},
stateful.States{A},
)
```Everything is done! Now run the machine:
```go
_ := stateMachine.Run(
// The transition function
myMachine.FromAToB,
// The transition params which will be passed to the transition function
stateful.TransitionArguments(AmountParams{Amount: 1}),
)_ = stateMachine.Run(
myMachine.FromBToA,
stateful.TransitionArguments(AmountParams{Amount: 1}),
)err := stateMachine.Run(
myMachine.FromBToA,
stateful.TransitionArguments(AmountParams{Amount: 1}),
)// We cannot run the transition "FromBToA" from current state "A"...
if err != nil {
log.Fatal(err) // will print: you cannot run FromAToB from state A
}// We cannot transfer the machine with current transition to returned state "C"
err = stateMachine.Run(
myMachine.FromAToNotExistingC,
stateful.TransitionArguments(nil),
)if err != nil {
log.Fatal(err) // will print: you cannot transfer to state C
}
```That's it!
## Draw graph
You can draw a graph of your state machine in `dot` format of graphviz.
Just pass in your created statemachine into the StateMachineGraph.
```go
import "github.com/bykof/stateful/src/statefulGraph"
stateMachineGraph := statefulGraph.StateMachineGraph{StateMachine: *stateMachine}
_ = stateMachineGraph.DrawGraph()
```This will print following to the console:
```
digraph {
A->B[ label="FromAToB" ];
B->A[ label="FromBToA" ];
A;
B;
}
```which is actually this graph:

## Wildcards
You can also address wildcards as SourceStates or DestinationStates
```
stateMachine.AddTransition(
myMachine.FromBToAllStates,
stateful.States{B},
stateful.States{stateful.AllStates},
)
```This will give you the opportunity to jump e.g. B to AllStates.
*Keep in mind that `AllStates` creates a lot of complexity and maybe a missbehavior.
So use it only if you are knowing what you are doing*
## ExamplesHave a look at the examples: [examples](https://github.com/bykof/stateful/tree/master/examples)
## Credits
Thank you [calhoun](https://www.calhoun.io/) for the sweet gopher image!
## Run tests
```sh
go test ./...
```## Author
👤 **Michael Bykovski**
* Twitter: [@michaelbykovski](https://twitter.com/michaelbykovski)
* Github: [@bykof](https://github.com/bykof)## Show your support
Give a ⭐️ if this project helped you!
## 📝 License
Copyright © 2019 [Michael Bykovski](https://github.com/bykof).
This project is [MIT](https://opensource.org/licenses/MIT) licensed.