ffsm

package module
v1.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2019 License: MIT Imports: 8 Imported by: 1

README

FFSM

CI Status Go Report Card codecov

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

Features

Quickstart

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

Examples

Finite state machine example

Simple FSM

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"
)
FSM with transition handlers

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"
)
More examples

See more in tests

Additional packages

Version Policy

ffsm follows semantic versioning for the documented public API on stable releases. v1.2 is the latest stable version. Follows changelog.

License

MIT, see LICENSE.

Documentation

Index

Constants

View Source
const UnknownState = ""

UnknownState it is value is an undefined state.

Variables

View Source
var (
	// ErrNotInitalState is the error returned by Machine when the is
	// not have initial state of Machine.
	ErrNotInitalState = errors.New("Is not set initial value of state")

	// ErrCtxCanceled is the error when context is canceled.
	ErrCtxCanceled = errors.New("Context canceled")

	// ErrNotRegTransition is the error returned by Machine from Dispatch method when the is
	// have not rules for current transition (src->dst not have actions).
	ErrNotRegTransition = errors.New("Not registred transition")
)
View Source
var DefaultToDispatchCap = 8

DefaultToDispatchCap default capacity of channel for new instance FSM.

NOTE: set this value depending on your needs.

Functions

func GetDstState

func GetDstState(ctx context.Context) string

GetDstState returns destinate state from context.

func GetSrcState

func GetSrcState(ctx context.Context) string

GetSrcState returns source state from context.

Types

type DispatchError

type DispatchError struct {
	ActionName        string
	SrcState          string
	DstState          string
	Err               error
	IsPanic           bool
	PanicStackRuntime string
}

DispatchError is the container with custom errors for dispatcher.

func (DispatchError) Error

func (e DispatchError) Error() string

type Dispatcher added in v1.1.0

type Dispatcher func(ctx context.Context, next string) (chan error, context.CancelFunc)

Dispatcher dispatcher of finite state machine.

type FSM added in v1.1.0

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

FSM finite state machine.

func NewFSM added in v1.1.0

func NewFSM(wf Stack, initState string) *FSM

NewFSM returns new finite state machine with initial state.

func (*FSM) AsyncDispatch added in v1.2.0

func (e *FSM) AsyncDispatch(ctx context.Context, next string) (chan error, context.CancelFunc)

AsyncDispatch dispatcher of finite state machine (thread-safe). Returns the channel for feedback and the function of cancel of transition context.

func (*FSM) Collect added in v1.1.0

func (e *FSM) Collect(ch chan<- prometheus.Metric)

func (*FSM) Describe added in v1.1.0

func (e *FSM) Describe(ch chan<- *prometheus.Desc)

func (*FSM) Dispatch added in v1.1.0

func (e *FSM) Dispatch(ctx context.Context, next string) error

Dispatch dispatch and wait for completion.

func (*FSM) SetName added in v1.1.0

func (e *FSM) SetName(name string)

SetName sets name of FSM (for prometheus labels).

func (*FSM) SetState added in v1.1.0

func (e *FSM) SetState(newState string)

SetState sets new state.

func (*FSM) Size added in v1.1.0

func (e *FSM) Size() uint64

Size returns number of messages in the queue (thread-safe).

func (*FSM) State added in v1.1.0

func (e *FSM) State() string

State returns current state.

func (*FSM) Stop added in v1.1.0

func (e *FSM) Stop()

Stop stops finite state machine.

type Procedure

type Procedure func(ctx context.Context) (context.Context, error)

Procedure handler of transition.

type Stack

type Stack map[StackKey][]Procedure

Stack actions of transition.

func (Stack) Add

func (r Stack) Add(src string, dst string, p ...Procedure) Stack

Add registration action.

func (Stack) Get

func (r Stack) Get(src, dst string) []Procedure

Get return actions of event.

type StackKey

type StackKey struct {
	Src string
	Dst string
}

StackKey is the identifier of the transition.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL