fsm

package module
Version: v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2015 License: Apache-2.0 Imports: 1 Imported by: 0

README

wercker status GoDoc

Disclaimer

This is a fork from https://github.com/looplab/fsm, keeping everything identical except that PRs are merged in.

FSM for Go

FSM is a finite state machine for Go.

It is heavily based on two FSM implementations:

For API docs and examples see http://godoc.org/github.com/looplab/fsm

Basic Example

From examples/simple.go:

package main

import (
    "fmt"
    "github.com/looplab/fsm"
)

func main() {
    fsm := fsm.NewFSM(
        "closed",
        fsm.Events{
            {Name: "open", Src: []string{"closed"}, Dst: "open"},
            {Name: "close", Src: []string{"open"}, Dst: "closed"},
        },
        fsm.Callbacks{},
    )

    fmt.Println(fsm.Current())

    err := fsm.Event("open")
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(fsm.Current())

    err = fsm.Event("close")
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(fsm.Current())
}

Usage as a struct field

From examples/struct.go:

package main

import (
    "fmt"
    "github.com/looplab/fsm"
)

type Door struct {
    To  string
    FSM *fsm.FSM
}

func NewDoor(to string) *Door {
    d := &Door{
        To: to,
    }

    d.FSM = fsm.NewFSM(
        "closed",
        fsm.Events{
            {Name: "open", Src: []string{"closed"}, Dst: "open"},
            {Name: "close", Src: []string{"open"}, Dst: "closed"},
        },
        fsm.Callbacks{
            "enter_state": func(e *fsm.Event) { d.enterState(e) },
        },
    )

    return d
}

func (d *Door) enterState(e *fsm.Event) {
    fmt.Printf("The door to %s is %s\n", d.To, e.Dst)
}

func main() {
    door := NewDoor("heaven")

    err := door.FSM.Event("open")
    if err != nil {
        fmt.Println(err)
    }

    err = door.FSM.Event("close")
    if err != nil {
        fmt.Println(err)
    }
}

License

FSM is licensed under Apache License 2.0

http://www.apache.org/licenses/LICENSE-2.0

Documentation

Overview

Package fsm implements a finite state machine.

It is heavily based on two FSM implementations:

Javascript Finite State Machine https://github.com/jakesgordon/javascript-state-machine

Fysom for Python https://github.com/oxplot/fysom (forked at https://github.com/mriehl/fysom)

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AsyncError

type AsyncError struct {
	Err error
}

AsyncError is returned by FSM.Event() when a callback have initiated an asynchronous state transition.

func (*AsyncError) Error

func (e *AsyncError) Error() string

type Callback

type Callback func(*Event)

Callback is a function type that callbacks should use. Event is the current event info as the callback happens.

type Callbacks

type Callbacks map[string]Callback

Callbacks is a shorthand for defining the callbacks in NewFSM.a

type CanceledError

type CanceledError struct {
	Err error
}

CanceledError is returned by FSM.Event() when a callback have canceled a transition.

func (*CanceledError) Error

func (e *CanceledError) Error() string

type Event

type Event struct {
	// FSM is a reference to the current FSM.
	FSM *FSM

	// Event is the event name.
	Event string

	// Src is the state before the transition.
	Src string

	// Dst is the state after the transition.
	Dst string

	// Err is an optional error that can be returned from a callback.
	Err error

	// Args is a optinal list of arguments passed to the callback.
	Args []interface{}
	// contains filtered or unexported fields
}

Event is the info that get passed as a reference in the callbacks.

func (*Event) Async

func (e *Event) Async()

Async can be called in leave_<STATE> to do an asynchronous state transition.

The current state transition will be on hold in the old state until a final call to Transition is made. This will comlete the transition and possibly call the other callbacks.

func (*Event) Cancel

func (e *Event) Cancel(err ...error)

Cancel can be called in before_<EVENT> or leave_<STATE> to cancel the current transition before it happens. It takes an opitonal error, which will overwrite e.Err if set before.

type EventDesc

type EventDesc struct {
	// Name is the event name used when calling for a transition.
	Name string

	// Src is a slice of source states that the FSM must be in to perform a
	// state transition.
	Src []string

	// Dst is the destination state that the FSM will be in if the transition
	// succeds.
	Dst string
}

EventDesc represents an event when initializing the FSM.

The event can have one or more source states that is valid for performing the transition. If the FSM is in one of the source states it will end up in the specified destination state, calling all defined callbacks as it goes.

type Events

type Events []EventDesc

Events is a shorthand for defining the transition map in NewFSM.

type FSM

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

FSM is the state machine that holds the current state.

It has to be created with NewFSM to function properly.

func NewFSM

func NewFSM(initial string, events []EventDesc, callbacks map[string]Callback) *FSM

NewFSM constructs a FSM from events and callbacks.

The events and transitions are specified as a slice of Event structs specified as Events. Each Event is mapped to one or more internal transitions from Event.Src to Event.Dst.

Callbacks are added as a map specified as Callbacks where the key is parsed as the callback event as follows, and called in the same order:

1. before_<EVENT> - called before event named <EVENT>

2. before_event - called before all events

3. leave_<OLD_STATE> - called before leaving <OLD_STATE>

4. leave_state - called before leaving all states

5. enter_<NEW_STATE> - called after eftering <NEW_STATE>

6. enter_state - called after entering all states

7. after_<EVENT> - called after event named <EVENT>

8. after_event - called after all events

There are also two short form versions for the most commonly used callbacks. They are simply the name of the event or state:

1. <NEW_STATE> - called after entering <NEW_STATE>

2. <EVENT> - called after event named <EVENT>

If both a shorthand version and a full version is specified it is undefined which version of the callback will end up in the internal map. This is due to the psuedo random nature of Go maps. No checking for multiple keys is currently performed.

Example
fsm := NewFSM(
	"green",
	Events{
		{Name: "warn", Src: []string{"green"}, Dst: "yellow"},
		{Name: "panic", Src: []string{"yellow"}, Dst: "red"},
		{Name: "panic", Src: []string{"green"}, Dst: "red"},
		{Name: "calm", Src: []string{"red"}, Dst: "yellow"},
		{Name: "clear", Src: []string{"yellow"}, Dst: "green"},
	},
	Callbacks{
		"before_warn": func(e *Event) {
			fmt.Println("before_warn")
		},
		"before_event": func(e *Event) {
			fmt.Println("before_event")
		},
		"leave_green": func(e *Event) {
			fmt.Println("leave_green")
		},
		"leave_state": func(e *Event) {
			fmt.Println("leave_state")
		},
		"enter_yellow": func(e *Event) {
			fmt.Println("enter_yellow")
		},
		"enter_state": func(e *Event) {
			fmt.Println("enter_state")
		},
		"after_warn": func(e *Event) {
			fmt.Println("after_warn")
		},
		"after_event": func(e *Event) {
			fmt.Println("after_event")
		},
	},
)
fmt.Println(fsm.Current())
err := fsm.Event("warn")
if err != nil {
	fmt.Println(err)
}
fmt.Println(fsm.Current())
Output:

green
before_warn
before_event
leave_green
leave_state
enter_yellow
enter_state
after_warn
after_event
yellow

func (*FSM) Can

func (f *FSM) Can(event string) bool

Can returns true if event can occur in the current state.

Example
fsm := NewFSM(
	"closed",
	Events{
		{Name: "open", Src: []string{"closed"}, Dst: "open"},
		{Name: "close", Src: []string{"open"}, Dst: "closed"},
	},
	Callbacks{},
)
fmt.Println(fsm.Can("open"))
fmt.Println(fsm.Can("close"))
Output:

true
false

func (*FSM) Cannot

func (f *FSM) Cannot(event string) bool

Cannot returns true if event can not occure in the current state. It is a convenience method to help code read nicely.

Example
fsm := NewFSM(
	"closed",
	Events{
		{Name: "open", Src: []string{"closed"}, Dst: "open"},
		{Name: "close", Src: []string{"open"}, Dst: "closed"},
	},
	Callbacks{},
)
fmt.Println(fsm.Cannot("open"))
fmt.Println(fsm.Cannot("close"))
Output:

false
true

func (*FSM) Current

func (f *FSM) Current() string

Current returns the current state of the FSM.

Example
fsm := NewFSM(
	"closed",
	Events{
		{Name: "open", Src: []string{"closed"}, Dst: "open"},
		{Name: "close", Src: []string{"open"}, Dst: "closed"},
	},
	Callbacks{},
)
fmt.Println(fsm.Current())
Output:

closed

func (*FSM) Event

func (f *FSM) Event(event string, args ...interface{}) error

Event initiates a state transition with the named event.

The call takes a variable number of arguments that will be passed to the callback, if defined.

It will return nil if the state change is ok or one of these errors:

- event X inappropriate because previous transition did not complete

- event X inappropriate in current state Y

- event X does not exist

- internal error on state transition

The last error should never occur in this situation and is a sign of an internal bug.

Example
fsm := NewFSM(
	"closed",
	Events{
		{Name: "open", Src: []string{"closed"}, Dst: "open"},
		{Name: "close", Src: []string{"open"}, Dst: "closed"},
	},
	Callbacks{},
)
fmt.Println(fsm.Current())
err := fsm.Event("open")
if err != nil {
	fmt.Println(err)
}
fmt.Println(fsm.Current())
err = fsm.Event("close")
if err != nil {
	fmt.Println(err)
}
fmt.Println(fsm.Current())
Output:

closed
open
closed

func (*FSM) Is

func (f *FSM) Is(state string) bool

Is returns true if state is the current state.

Example
fsm := NewFSM(
	"closed",
	Events{
		{Name: "open", Src: []string{"closed"}, Dst: "open"},
		{Name: "close", Src: []string{"open"}, Dst: "closed"},
	},
	Callbacks{},
)
fmt.Println(fsm.Is("closed"))
fmt.Println(fsm.Is("open"))
Output:

true
false

func (*FSM) Transition

func (f *FSM) Transition() error

Transition wraps transitioner.transition.

Example
fsm := NewFSM(
	"closed",
	Events{
		{Name: "open", Src: []string{"closed"}, Dst: "open"},
		{Name: "close", Src: []string{"open"}, Dst: "closed"},
	},
	Callbacks{
		"leave_closed": func(e *Event) {
			e.Async()
		},
	},
)
err := fsm.Event("open")
if e, ok := err.(*AsyncError); !ok && e.Err != nil {
	fmt.Println(err)
}
fmt.Println(fsm.Current())
err = fsm.Transition()
if err != nil {
	fmt.Println(err)
}
fmt.Println(fsm.Current())
Output:

closed
open

type InTransitionError

type InTransitionError struct {
	Event string
}

InTransitionError is returned by FSM.Event() when an asynchronous transition is already in progress.

func (*InTransitionError) Error

func (e *InTransitionError) Error() string

type InternalError

type InternalError struct{}

InternalError is returned by FSM.Event() and should never occur. It is a probably because of a bug.

func (*InternalError) Error

func (e *InternalError) Error() string

type InvalidEventError

type InvalidEventError struct {
	Event string
	State string
}

InvalidEventError is returned by FSM.Event() when the event cannot be called in the current state.

func (*InvalidEventError) Error

func (e *InvalidEventError) Error() string

type NoTransitionError

type NoTransitionError struct {
	Err error
}

NoTransitionError is returned by FSM.Event() when no transition have happened, for example if the source and destination states are the same.

func (*NoTransitionError) Error

func (e *NoTransitionError) Error() string

type NotInTransitionError

type NotInTransitionError struct{}

NotInTransitionError is returned by FSM.Transition() when an asynchronous transition is not in progress.

func (*NotInTransitionError) Error

func (e *NotInTransitionError) Error() string

type UnknownEventError

type UnknownEventError struct {
	Event string
}

UnknownEventError is returned by FSM.Event() when the event is not defined.

func (*UnknownEventError) Error

func (e *UnknownEventError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL