README

Welcome to stateful 👋

Travis CI

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

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.

Expand ▾ Collapse ▴

Documentation

Index

Constants

View Source
const (
	// AllStates is a wildcard which represents all states in the state machine
	AllStates = DefaultState("*")
)

Variables

This section is empty.

Functions

This section is empty.

Types

type CannotRunFromStateError

type CannotRunFromStateError struct {
	// contains filtered or unexported fields
}

func NewCannotRunFromStateError

func NewCannotRunFromStateError(stateMachine StateMachine, transitionRule TransitionRule) *CannotRunFromStateError

func (CannotRunFromStateError) Error

func (crfse CannotRunFromStateError) Error() string

type CannotTransferToStateError

type CannotTransferToStateError struct {
	// contains filtered or unexported fields
}

func NewCannotTransferToStateError

func NewCannotTransferToStateError(state State) *CannotTransferToStateError

func (CannotTransferToStateError) Error

func (cttse CannotTransferToStateError) Error() string

type DefaultState

type DefaultState string

    DefaultState is a string which should be used in every stateful object as the state

    func (DefaultState) GetID

    func (ds DefaultState) GetID() string

      GetID returns the string representation of the DefaultState

      func (DefaultState) IsWildCard

      func (ds DefaultState) IsWildCard() bool

        IsWildCard checks if the current state is a wildcard. So if the state stands for all possible states

        type State

        type State interface {
        	GetID() string
        	IsWildCard() bool
        }

          State represents a state of a stateful object

          type StateMachine

          type StateMachine struct {
          	StatefulObject Stateful
          	// contains filtered or unexported fields
          }

            StateMachine handles the state of the StatefulObject

            func (*StateMachine) AddTransition

            func (sm *StateMachine) AddTransition(
            	transition Transition,
            	sourceStates States,
            	destinationStates States,
            )

              AddTransition adds a transition to the state machine.

              func (StateMachine) GetAllStates

              func (sm StateMachine) GetAllStates() States

                GetAllStates returns all known and possible states by the state machine

                func (StateMachine) GetAvailableTransitions

                func (sm StateMachine) GetAvailableTransitions() Transitions

                func (StateMachine) GetTransitionRules

                func (sm StateMachine) GetTransitionRules() TransitionRules

                  GetTransitionRules returns all transitionRules in the state machine

                  func (StateMachine) Run

                  func (sm StateMachine) Run(
                  	transition Transition,
                  	transitionArguments TransitionArguments,
                  ) error

                    Run runs the state machine with the given transition. If the transition

                    type Stateful

                    type Stateful interface {
                    	// State returns the current state of the stateful object
                    	State() State
                    
                    	// SetState sets the state of the stateful object and returns an error if it fails
                    	SetState(state State) error
                    }

                      Stateful is the core interface which should be implemented by all stateful structs. If this interface is implemented by a struct it can be processed by the state machine

                      type States

                      type States []State

                        States are a slice of State

                        func (States) Contains

                        func (ss States) Contains(state State) bool

                          Contains search in States if the given state is inside the States. It compares with GetID

                          func (States) HasWildCard

                          func (ss States) HasWildCard() bool

                            HasWildCard checks if there is a wildcard state inside of States

                            type Transition

                            type Transition func(transitionArguments TransitionArguments) (State, error)

                              Transition represents the transition function which will be executed if the order is in the proper state and there is a valid transitionRule in the state machine

                              func (Transition) GetID

                              func (t Transition) GetID() uintptr

                              func (Transition) GetName

                              func (t Transition) GetName() string

                              type TransitionArguments

                              type TransitionArguments interface{}

                                TransitionArguments represents the arguments

                                type TransitionRule

                                type TransitionRule struct {
                                	SourceStates      States
                                	Transition        Transition
                                	DestinationStates States
                                }

                                func (TransitionRule) IsAllowedToRun

                                func (tr TransitionRule) IsAllowedToRun(state State) bool

                                func (TransitionRule) IsAllowedToTransfer

                                func (tr TransitionRule) IsAllowedToTransfer(state State) bool

                                type TransitionRuleNotFoundError

                                type TransitionRuleNotFoundError struct {
                                	// contains filtered or unexported fields
                                }

                                func NewTransitionRuleNotFoundError

                                func NewTransitionRuleNotFoundError(transition Transition) *TransitionRuleNotFoundError

                                func (TransitionRuleNotFoundError) Error

                                func (trnfe TransitionRuleNotFoundError) Error() string

                                type TransitionRules

                                type TransitionRules []TransitionRule

                                func (TransitionRules) Find

                                func (trs TransitionRules) Find(transition Transition) *TransitionRule

                                type Transitions

                                type Transitions []Transition

                                  Transitions are a slice of Transition

                                  func (Transitions) Contains

                                  func (ts Transitions) Contains(transition Transition) bool

                                  Directories

                                  Path Synopsis
                                  examples