logo   async
machine.dev

/pkg/states/README.md

/pkg/states contains common state schema mixins to make state-based APIs easier to compose and exchange. It also offers tooling for piping states between state machines.

Available State Schemas

Installation States

import ssam "github.com/pancsta/asyncmachine-go/pkg/states"

Examples

Inherit from BasicStatesDef manually

// inherit BasicSchema
schema := am.SchemaMerge(ssam.BasicSchema, am.Schema{
    "Foo": {Require: am.S{"Bar"}},
    "Bar": {},
})
names := am.SAdd(ssam.BasicStates.Names(), am.S{"Foo", "Bar"})

Inherit from BasicStatesDef via a schema definition

// MyMachStatesDef contains all the states of the MyMach state machine.
type MyMachStatesDef struct {
    *am.StatesBase

    State1 string
    State2 string

    // inherit from BasicStatesDef
    *ss.BasicStatesDef
}

// MyMachSchema represents all relations and properties of MyMachStates.
var MyMachSchema = SchemaMerge(
    // inherit from BasicSchema
    ss.BasicSchema,
    am.Schema{

        ssM.State1: {},
        ssM.State2: {
            Multi: true,
        },
})

Inherit from BasicStatesDef via the generator

$ am-gen --name MyMach \
  --states State1,State2 \
  --inherit basic

Piping

A “pipe” binds a transition handler to a source state-machine and mutates the target state-machine. Only final handlers are subject to piping and the resulting mutation won’t block the source transition (it will be queued instead). The target state-machine can reject the mutation, as a part of the negotiation phase.

Pipes work only within the same Golang process, but when combined with /pkg/rpc, we can effectively pipe states over the network. Some packages export predefined pipes for their state machines (eg /pkg/rpc and /pkg/node).

Installation Pipes

import ampipe "github.com/pancsta/asyncmachine-go/pkg/states/pipes"

Predefined Pipes

These predefined functions should cover most of the use cases:

  • BindErr
  • BindReady
  • BindConnected
  • Bind
  • BindMany
  • BindAny

Using Pipes

// pipe RpcReady to RpcReady from rpcClient.Mach to myMach
ampipe.BindReady(rpcClient.Mach, myMach, "RpcReady", "")

// pipe Foo to FooActive and FooInactive from myMach1 to myMach2
ampipe.Bind(myMach1, myMach2, "Foo", "FooActive", "FooInactive")

Piping Manually

var source *am.Machine
var target *am.Machine

h := &struct {
    ReadyState am.HandlerFinal
    ReadyEnd   am.HandlerFinal
}{
    ReadyState: ampipe.Add(source, target, "Ready", "RpcReady"),
    ReadyEnd:   ampipe.Remove(source, target, "Ready", "RpcReady"),
}

source.BindHandlers(h)

Extending States

The purpose of this package is to share reusable schemas, but inheriting a full schema isn’t enough. States can also be extended on relations-level using helpers from states_utils.go (generated), although sometimes it’s better to override a state (eg Ready).

import (
    am "github.com/pancsta/asyncmachine-go/pkg/machine"
    "github.com/pancsta/asyncmachine-go/pkg/states"
    ampipe "github.com/pancsta/asyncmachine-go/pkg/states/pipes"
)

// ...

MyMychStruct = am.Schema{

    // inject Client states into Connected
    "Connected": StateAdd(
        states.ConnectedStruct[states.ConnectedStates.Connected],
        am.State{
            Remove: S{"RetryingConn"},
            Add:    S{"Handshaking"},
    }),
}

// see state_utils.go

Documentation

Status

Testing, semantically versioned.