Skip to content
Finite state machine for Go
Go
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
docs/resources New gopher logo and credits Jul 24, 2019
examples Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019
statefulGraph Fix module naming and import paths of examples Jul 29, 2019
.gitignore Initial commit Jul 18, 2019
.travis.yml remove go mod vendor Jul 18, 2019
LICENSE Initial commit Jul 18, 2019
README.md Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019
errors.go Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019
go.mod Fix module naming and import paths of examples Jul 29, 2019
go.sum Initial commit Jul 18, 2019
state.go Add some documentation Jul 31, 2019
stateMachine.go Add some documentation Jul 31, 2019
stateMachine_test.go Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019
state_test.go Move all stateful modules to top layer Jul 21, 2019
stateful.go Add some documentation Jul 31, 2019
transition.go Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019
transitionRule.go
transitionRule_test.go Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019
transition_test.go Rename interface TransitionArgs to TransitionArguments and GetState()… Jul 30, 2019

README.md

Welcome to stateful 👋

Travis CI License: MIT

Twitter: michaelbykovski

Create easy state machines with your existing code

Table of Contents

  1. Documentation
  2. Usage
  3. Draw graph
  4. Wildcards
  5. Examples
  6. Credits

Documentation

You can find the documentation here: https://godoc.org/github.com/bykof/stateful

Usage

It is very easy to use stateful. Just create a struct and implement the stateful interface

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:

const (
    A = stateful.DefaultState("A")
    B = stateful.DefaultState("B")
)

Then add some transitions to the machine:

// 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:

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:

_ := 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.

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:

MyMachine Transition 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

Examples

Have a look at the examples: examples

Credits

Thank you calhoun for the sweet gopher image!

Run tests

go test ./...

Author

👤 Michael Bykovski

Show your support

Give a ⭐️ if this project helped you!

📝 License

Copyright © 2019 Michael Bykovski.
This project is MIT licensed.

You can’t perform that action at this time.