Documentation
¶
Index ¶
- Constants
- Variables
- func ToMermaid[S ~string, E ~string, D Cloner[D]](m *Machine[S, E, D], opts ...MermaidOption) string
- func ToSCXMLBytes[S ~string, E ~string, D Cloner[D]](m *Machine[S, E, D]) ([]byte, error)
- func ToSCXMLString[S ~string, E ~string, D Cloner[D]](m *Machine[S, E, D]) (string, error)
- type ActionEvent
- type ActionObserver
- type Actor
- func (a *Actor[S, E, D]) Data() D
- func (a *Actor[S, E, D]) ID() ActorID
- func (a *Actor[S, E, D]) Send(event E)
- func (a *Actor[S, E, D]) SendCtx(ctx context.Context, event E) error
- func (a *Actor[S, E, D]) Snapshot() Snapshot[S, D]
- func (a *Actor[S, E, D]) State() S
- func (a *Actor[S, E, D]) States() []S
- func (a *Actor[S, E, D]) Stop()
- type ActorID
- type BaseObserver
- type Cloner
- type EventDroppedObserver
- type EventNotice
- type EventReceivedObserver
- type GuardEvent
- type GuardObserver
- type HistoryType
- type InvokeCompletedObserver
- type InvokeDef
- type InvokeEvent
- type InvokeStartedObserver
- type Machine
- type MachineBuilder
- type MermaidOption
- type MermaidThemeName
- type NodeKind
- type Observer
- type ObserverFuncs
- func (o ObserverFuncs[S, E, D]) OnActionExecuted(ctx context.Context, e *ActionEvent[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnEventDropped(ctx context.Context, e *EventNotice[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnEventReceived(ctx context.Context, e *EventNotice[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnGuardEvaluated(ctx context.Context, e *GuardEvent[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnInvokeCompleted(ctx context.Context, e *InvokeEvent[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnInvokeStarted(ctx context.Context, e *InvokeEvent[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnStateEntered(ctx context.Context, e *StateEvent[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnStateExited(ctx context.Context, e *StateEvent[S, E, D])
- func (o ObserverFuncs[S, E, D]) OnTransition(ctx context.Context, e *TransitionEvent[S, E, D])
- type Option
- type RecordedEvent
- type RecordingObserver
- func (r *RecordingObserver[S, E, D]) Actions() []*ActionEvent[S, E, D]
- func (r *RecordingObserver[S, E, D]) Events(kinds ...string) []RecordedEvent
- func (r *RecordingObserver[S, E, D]) EventsDropped() []*EventNotice[S, E, D]
- func (r *RecordingObserver[S, E, D]) EventsReceived() []*EventNotice[S, E, D]
- func (r *RecordingObserver[S, E, D]) Guards() []*GuardEvent[S, E, D]
- func (r *RecordingObserver[S, E, D]) InvokeCompleted() []*InvokeEvent[S, E, D]
- func (r *RecordingObserver[S, E, D]) InvokeStarted() []*InvokeEvent[S, E, D]
- func (r *RecordingObserver[S, E, D]) OnActionExecuted(_ context.Context, e *ActionEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnEventDropped(_ context.Context, e *EventNotice[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnEventReceived(_ context.Context, e *EventNotice[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnGuardEvaluated(_ context.Context, e *GuardEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnInvokeCompleted(_ context.Context, e *InvokeEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnInvokeStarted(_ context.Context, e *InvokeEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnStateEntered(_ context.Context, e *StateEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnStateExited(_ context.Context, e *StateEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) OnTransition(_ context.Context, e *TransitionEvent[S, E, D])
- func (r *RecordingObserver[S, E, D]) Reset()
- func (r *RecordingObserver[S, E, D]) StateEntered() []*StateEvent[S, E, D]
- func (r *RecordingObserver[S, E, D]) StateExited() []*StateEvent[S, E, D]
- func (r *RecordingObserver[S, E, D]) Transitions() []*TransitionEvent[S, E, D]
- type SCXMLAssign
- type SCXMLDocument
- type SCXMLInvoke
- type SCXMLNode
- type SCXMLOnEntry
- type SCXMLOnExit
- type SCXMLSend
- type SCXMLTransition
- type Snapshot
- type StateBuilder
- func (s *StateBuilder[S, E, D]) After(d time.Duration) *TransitionBuilder[S, E, D]
- func (s *StateBuilder[S, E, D]) Always() *TransitionBuilder[S, E, D]
- func (s *StateBuilder[S, E, D]) Entry(fn func(D) D)
- func (s *StateBuilder[S, E, D]) EntryLabel(name string)
- func (s *StateBuilder[S, E, D]) Exit(fn func(D) D)
- func (s *StateBuilder[S, E, D]) ExitLabel(name string)
- func (s *StateBuilder[S, E, D]) History(t HistoryType)
- func (s *StateBuilder[S, E, D]) Initial(id S)
- func (s *StateBuilder[S, E, D]) Invoke(fn func(ctx context.Context, snap D, mutate func(func(D) D)) error, onDone S, ...)
- func (s *StateBuilder[S, E, D]) InvokeLabel(name string)
- func (s *StateBuilder[S, E, D]) On(event E) *TransitionBuilder[S, E, D]
- func (s *StateBuilder[S, E, D]) State(id S, fn func(*StateBuilder[S, E, D]))
- func (s *StateBuilder[S, E, D]) Type(t StateType)
- type StateDef
- type StateEnteredObserver
- type StateEvent
- type StateExitedObserver
- type StateType
- type TransitionBuilder
- func (t *TransitionBuilder[S, E, D]) ActionLabel(name string) *TransitionBuilder[S, E, D]
- func (t *TransitionBuilder[S, E, D]) Assign(fn func(D) D) *TransitionBuilder[S, E, D]
- func (t *TransitionBuilder[S, E, D]) GoTo(target S)
- func (t *TransitionBuilder[S, E, D]) Guard(fn func(D) bool) *TransitionBuilder[S, E, D]
- func (t *TransitionBuilder[S, E, D]) GuardLabel(name string) *TransitionBuilder[S, E, D]
- type TransitionDef
- type TransitionEvent
- type TransitionObserver
Examples ¶
Constants ¶
const ( KindTransition = "transition" KindGuardEvaluated = "guard" KindInvokeStarted = "invoke_started" KindInvokeCompleted = "invoke_completed" KindStateEntered = "state_entered" KindStateExited = "state_exited" KindActionExecuted = "action" KindEventReceived = "event_received" KindEventDropped = "event_dropped" )
Kind constants identify the entry type in [RecordedEvent.Kind] and are used to filter RecordingObserver.Events.
Variables ¶
var ErrActorStopped = errors.New("gstate: actor stopped")
ErrActorStopped is returned by Actor.SendCtx when the actor has already been stopped and the event cannot be delivered. It is distinct from a context-cancelled error so callers can branch on the reason an event was not delivered.
Functions ¶
func ToMermaid ¶
func ToMermaid[S ~string, E ~string, D Cloner[D]](m *Machine[S, E, D], opts ...MermaidOption) string
ToMermaid converts a Machine to a Mermaid flowchart source string. The output is plain Mermaid syntax; downstream consumers (GitHub README, mermaid.live, mermaid-cli, IDE plugins) handle the actual rendering.
func ToSCXMLBytes ¶
ToSCXMLBytes converts a Machine to SCXML bytes with XML header.
Types ¶
type ActionEvent ¶
type ActionEvent[S ~string, E ~string, D Cloner[D]] struct { MachineID string `json:"machine_id"` ActorID ActorID `json:"actor_id"` // State is the source state of the firing transition. State S `json:"state"` // Event is the triggering event. Zero value for Always / internal triggers. Event E `json:"event,omitempty"` // Target is the destination state ID, or zero for internal transitions. Target S `json:"target,omitempty"` Timestamp time.Time `json:"timestamp"` // contains filtered or unexported fields }
ActionEvent is the payload for [Observer.OnActionExecuted]. It is emitted only when a transition has a non-nil Action.
func (*ActionEvent[S, E, D]) Data ¶ added in v0.3.0
func (e *ActionEvent[S, E, D]) Data() *D
func (*ActionEvent[S, E, D]) MarshalJSON ¶ added in v0.3.0
func (e *ActionEvent[S, E, D]) MarshalJSON() ([]byte, error)
func (*ActionEvent[S, E, D]) String ¶
func (e *ActionEvent[S, E, D]) String() string
String renders the action as "action[ActorID]: State --Event--> Target" (Target is "<internal>" when empty).
type ActionObserver ¶ added in v0.3.0
type Actor ¶
Actor is the runtime interpreter for a statechart machine. It maintains the current state, processes events sequentially, and manages asynchronous services (invocations and timers).
func Hydrate ¶
func Hydrate[S ~string, E ~string, D Cloner[D]](m *Machine[S, E, D], snapshot Snapshot[S, D], opts ...Option[S, E, D]) *Actor[S, E, D]
Hydrate restores an Actor from a previously captured Snapshot. It restarts any services (invocations/timers) associated with the active states without re-executing state entry actions. The same Option set as Start is accepted so callers can attach an observer or tune the mailbox on a hydrated actor.
Hydrate does not fire [Observer.OnStateEntered] or [Observer.OnTransition] for the states restored from the snapshot — those events represent the original state changes that were already observed before the snapshot was captured. Hooks resume firing on the next event, Always evaluation, or invoke completion handled by the hydrated actor.
The ActorID is resolved in priority order: [WithActorID] if supplied, otherwise the ActorID stored in the snapshot.
func Start ¶
func Start[S ~string, E ~string, D Cloner[D]](m *Machine[S, E, D], initialData D, opts ...Option[S, E, D]) *Actor[S, E, D]
Start creates and launches a new Actor for the given machine. Options are applied in order; later values for the same option win. The returned Actor is already running and ready to receive events via Actor.Send or Actor.SendCtx.
func (*Actor[S, E, D]) Data ¶ added in v0.3.0
func (a *Actor[S, E, D]) Data() D
Data returns a thread-safe copy of the current data. Thread safety is guaranteed by calling [Cloner.Clone] on the underlying data.
func (*Actor[S, E, D]) ID ¶
ID returns the actor's stable identifier. The ID is generated on Start (unless overridden with [WithActorID]) and is preserved across Actor.Snapshot and Hydrate so telemetry can correlate the same logical actor across persistence boundaries.
func (*Actor[S, E, D]) Send ¶
func (a *Actor[S, E, D]) Send(event E)
Send enqueues an event in the actor's mailbox using context.Background as the request-scoped context. It is a thin wrapper over Actor.SendCtx that discards the returned error; with context.Background the only possible non-nil return is ErrActorStopped, in which case the send is a no-op per Stop's contract (no panic, no delivery).
Callers that need to react to delivery failure should use Actor.SendCtx directly.
func (*Actor[S, E, D]) SendCtx ¶
SendCtx enqueues an event in the actor's mailbox carrying the supplied request-scoped context. The context is threaded into every Observer callback fired in response to this event (including Always transitions chained after it) AND it gates the enqueue itself.
Returns:
- nil when the event was enqueued.
- ctx.Err() (context.Canceled or context.DeadlineExceeded) when the supplied context was cancelled or its deadline elapsed before the event could be enqueued. The event is NOT delivered.
- ErrActorStopped when the actor was stopped before the event could be enqueued. The event is NOT delivered.
Behaviour when the mailbox is full: SendCtx blocks until one of three things happens — a slot opens, ctx is done, or the actor is stopped. It never blocks forever.
func (*Actor[S, E, D]) Snapshot ¶
Snapshot captures the current status of the Actor, including its active states, history data, and data. The returned struct is suitable for JSON serialization.
func (*Actor[S, E, D]) State ¶
func (a *Actor[S, E, D]) State() S
State returns the ID of the current deepest active leaf state. In the case of parallel states, it returns one of the active leaf states.
func (*Actor[S, E, D]) States ¶
func (a *Actor[S, E, D]) States() []S
States returns the current list of all active states, ordered from root to leaf.
func (*Actor[S, E, D]) Stop ¶
func (a *Actor[S, E, D]) Stop()
Stop shuts the actor down and waits for all goroutines it owns to exit before returning. It is safe to call from multiple goroutines and safe to call more than once — only the first call performs the shutdown work.
Stop completion contract:
Guaranteed finished before Stop returns:
- Entry, exit, and transition actions, and guard evaluations, for any event the actor had already begun processing. These run synchronously under the actor's write lock; Stop acquires that lock before signalling shutdown, so the in-flight transition completes first.
- InvokeDef Func goroutines. Stop cancels their context.Context and waits for each Func function to return.
- [Observer.OnInvokeCompleted] callbacks for each in-flight or cancelled invoke. They fire from inside the invoke goroutine immediately before the WaitGroup decrement.
- [Observer.OnStateExited] and [Observer.OnStateEntered] callbacks fired during the in-flight transition.
Not awaited by Stop:
- Events buffered in the mailbox that the actor had not yet pulled. They are abandoned: once Stop has signalled shutdown, the loop exits without consuming further events. Use the invoke pattern below if you have work that must run before shutdown.
- Goroutines spawned by user code from inside an action, guard, or invoke (for example `go publishMetric(c)` inside an Assign action). The actor has no handle on them. If the work must finish before Stop returns, model it as an InvokeDef Func instead — Stop awaits invoke goroutines.
- time.AfterFunc callbacks for delayed transitions that were already firing when Stop ran. They safely no-op via the inactive-state check in executeInternalTransition but Stop does not block for them.
Send after Stop is a no-op. Actor.Send and Actor.SendCtx called after Stop (or concurrently with Stop past the shutdown-signal point) return without delivering the event and without panicking.
type ActorID ¶
type ActorID string
ActorID is the stable identifier for a running Actor. It is generated on Start (unless overridden via [WithActorID]) and survives Actor.Snapshot and Hydrate so telemetry can correlate across persistence boundaries.
type BaseObserver ¶ added in v0.3.0
BaseObserver is the marker-implementing zero struct. Embed it in your observer type to satisfy Observer. Provides no callback methods — implement whichever narrow interfaces you care about directly.
type Cloner ¶
type Cloner[T any] interface { Clone() T }
Cloner is required of every data type used with a Machine. Clone() must return an independent deep copy: mutations to the returned value must not be observable through any reference to the original. For struct types containing no references (no pointers, slices, maps, channels, or funcs), func (c T) Clone() T { return c } is sufficient. For pointer types, ensure referenced data (slices, maps, nested structs) is also deep-copied.
type EventDroppedObserver ¶ added in v0.3.0
type EventNotice ¶
type EventNotice[S ~string, E ~string, D Cloner[D]] struct { MachineID string `json:"machine_id"` ActorID ActorID `json:"actor_id"` Event E `json:"event"` // Reason describes why the event was dropped (e.g. "no_transition"). It is // empty on OnEventReceived. Reason string `json:"reason,omitempty"` Timestamp time.Time `json:"timestamp"` }
EventNotice is the payload for [Observer.OnEventReceived] and [Observer.OnEventDropped].
func (EventNotice[S, E, D]) String ¶
func (e EventNotice[S, E, D]) String() string
String renders the event notice as "event[ActorID]: Event" or, when a reason is set (i.e. drop notices), "event[ActorID]: Event reason=...".
type EventReceivedObserver ¶ added in v0.3.0
type GuardEvent ¶
type GuardEvent[S ~string, E ~string, D Cloner[D]] struct { MachineID string `json:"machine_id"` ActorID ActorID `json:"actor_id"` State S `json:"state"` // Event is the triggering event. Zero value for Always guards. Event E `json:"event,omitempty"` Target S `json:"target"` Result bool `json:"result"` Timestamp time.Time `json:"timestamp"` // contains filtered or unexported fields }
GuardEvent is the payload for [Observer.OnGuardEvaluated]. It is emitted only when the transition defines a non-nil Guard, so the absence of an event does not imply the absence of guard evaluation.
func (*GuardEvent[S, E, D]) Data ¶ added in v0.3.0
func (e *GuardEvent[S, E, D]) Data() *D
func (*GuardEvent[S, E, D]) MarshalJSON ¶ added in v0.3.0
func (e *GuardEvent[S, E, D]) MarshalJSON() ([]byte, error)
func (*GuardEvent[S, E, D]) String ¶
func (e *GuardEvent[S, E, D]) String() string
String renders the guard as "guard[ActorID]: State --Event[Target]: result=true|false".
type GuardObserver ¶ added in v0.3.0
type HistoryType ¶
type HistoryType int
HistoryType represents the type of history tracking for a compound state. When history is enabled, entering a compound state will restore the last active child instead of defaulting to the initial state.
const ( // None means no history is tracked for the state. None HistoryType = iota // Shallow remembers the direct child state that was active. Shallow // Deep remembers all active descendants in the state hierarchy. Deep )
type InvokeCompletedObserver ¶ added in v0.3.0
type InvokeDef ¶
type InvokeDef[S ~string, E ~string, D Cloner[D]] struct { // Func is the function to run. It receives a context cancelled on state exit, // a defensive snapshot of the data taken at state entry, and a thread-safe // mutate callback for applying writes to the live data. // // Parameters: // - ctx: cancelled when the state exits or the actor stops. Standard context.Context semantics. // - snap: a deep copy of the actor's data taken at the moment of state entry, via D.Clone(). // Reads are lock-free and never race because the invoke goroutine owns this value. // - mutate: applies updates to the live actor data under the actor's write lock. // The result of the mutation function replaces the live data. mutate is synchronous // and returns after the write commits. It no-ops if the state has exited or the actor stopped. // // This field was previously named Src. Func func(ctx context.Context, snap D, mutate func(func(D) D)) error // OnDone is the target state when Func returns nil. OnDone S // OnError is the target state when Func returns a non-nil error. OnError S // contains filtered or unexported fields }
InvokeDef defines an asynchronous service managed during a state's lifecycle. The service is started in a goroutine on state entry and cancelled on exit.
type InvokeEvent ¶
type InvokeEvent[S ~string, E ~string, D Cloner[D]] struct { MachineID string `json:"machine_id"` ActorID ActorID `json:"actor_id"` State S `json:"state"` // Error is nil on OnInvokeStarted and on successful completion. // On cancellation it is typically [context.Canceled]. It serializes to // JSON as its string form (or null when nil) — see [InvokeEvent.MarshalJSON]. Error error `json:"-"` // Duration is zero on OnInvokeStarted and the elapsed wall time on completion. Duration time.Duration `json:"duration"` Timestamp time.Time `json:"timestamp"` }
InvokeEvent is the payload for [Observer.OnInvokeStarted] and [Observer.OnInvokeCompleted].
func (InvokeEvent[S, E, D]) MarshalJSON ¶
func (e InvokeEvent[S, E, D]) MarshalJSON() ([]byte, error)
MarshalJSON renders Error as its Error() string (or null when nil) so the payload round-trips through standard JSON tooling. All other fields use their declared json tags.
func (InvokeEvent[S, E, D]) String ¶
func (e InvokeEvent[S, E, D]) String() string
String renders the invoke event as "invoke[ActorID]: state=... duration=... error=...". Fields with zero values are omitted.
type InvokeStartedObserver ¶ added in v0.3.0
type Machine ¶
type Machine[S ~string, E ~string, D Cloner[D]] struct { // ID identifies this machine definition. ID string // Initial is the state to enter when the machine starts. Initial S // States is a flat map of every state (including nested) for O(1) lookup. States map[S]*StateDef[S, E, D] }
Machine is the static, immutable definition of a statechart. It serves as a blueprint for creating Actor instances.
func (*Machine[S, E, D]) WithActorID ¶
WithActorID returns an Option that overrides the auto-generated ActorID for the actor. When omitted, a fresh ID is generated with nanoid. Supplying an empty string is treated as "no override".
func (*Machine[S, E, D]) WithMailboxSize ¶
WithMailboxSize returns an Option that sets the buffered capacity of the actor's event channel. When omitted, the default is 100. Values <= 0 fall back to the default.
func (*Machine[S, E, D]) WithObservers ¶ added in v0.3.0
WithObservers returns an Option that installs Observer values receiving lifecycle callbacks for the actor. The observers are invoked synchronously on the actor's event-processing goroutine; see Observer for the full threading contract.
type MachineBuilder ¶
type MachineBuilder[S ~string, E ~string, D Cloner[D]] struct { // contains filtered or unexported fields }
MachineBuilder provides a fluent API for declaring statechart definitions.
func New ¶
func New[S ~string, E ~string, D Cloner[D]](id string) *MachineBuilder[S, E, D]
New initiates the creation of a new statechart machine definition.
func (*MachineBuilder[S, E, D]) Build ¶
func (m *MachineBuilder[S, E, D]) Build() *Machine[S, E, D]
Build finalizes the machine definition and returns an immutable Machine instance. It performs a static-analysis validation pass and panics if any invalid state, transition target, or invoke target is detected.
func (*MachineBuilder[S, E, D]) Initial ¶
func (m *MachineBuilder[S, E, D]) Initial(id S) *MachineBuilder[S, E, D]
Initial sets the starting state for the machine.
func (*MachineBuilder[S, E, D]) State ¶
func (m *MachineBuilder[S, E, D]) State(id S, fn func(*StateBuilder[S, E, D])) *MachineBuilder[S, E, D]
State defines a top-level state in the machine. The provided function is used to configure the state's behavior via a StateBuilder.
type MermaidOption ¶
type MermaidOption func(*mermaidConfig)
MermaidOption configures Mermaid diagram output.
func MermaidFontSize ¶
func MermaidFontSize(size int) MermaidOption
MermaidFontSize sets the base font size in pixels (default 16).
func MermaidTheme ¶
func MermaidTheme(theme MermaidThemeName) MermaidOption
MermaidTheme sets the Mermaid color theme.
func MermaidTitle ¶
func MermaidTitle(title string) MermaidOption
MermaidTitle sets the diagram title shown in the frontmatter.
type MermaidThemeName ¶
type MermaidThemeName string
MermaidThemeName identifies a Mermaid color theme.
const ( MermaidThemeDefault MermaidThemeName = "default" MermaidThemeNeutral MermaidThemeName = "neutral" MermaidThemeDark MermaidThemeName = "dark" MermaidThemeForest MermaidThemeName = "forest" MermaidThemeBase MermaidThemeName = "base" )
Mermaid theme constants matching the five built-in Mermaid themes.
type Observer ¶
type Observer[S ~string, E ~string, D Cloner[D]] interface { // contains filtered or unexported methods }
Observer is the marker interface for all statechart lifecycle hook listeners. To observe actor lifecycle events:
- Embed BaseObserver in your type.
- Implement any of the nine narrow observer interfaces (e.g. TransitionObserver, GuardObserver, etc.) to receive specific hooks.
- Register your observer by passing the output of Machine.WithObservers as an Option to Start.
Hook methods receive event structures containing metadata and an unexported copy of the actor's context data at the time of the event. Call `e.Data()` on any event payload to retrieve a pointer to this data. The copy is evaluated lazily on the first `Data()` call using sync.Once (via [Cloner.Clone()] when implemented, or value copying otherwise), so observers only pay for deep-copying if they read the data.
Threading and locking contract:
- All callbacks except OnInvokeCompleted run synchronously on the actor's event-processing goroutine while it holds the actor's internal write lock. This includes OnInvokeStarted, which fires from enterSingleState's service-restart path. Implementations must be non-blocking.
- Implementations must not call methods on the same Actor that would require re-entering the actor lock (e.g. Actor.Snapshot, Actor.State).
- Implementations must not call Actor.Send or Actor.SendCtx synchronously: the channel send can block on a full mailbox, and the loop goroutine that would drain it cannot acquire the actor lock the observer is holding — a hard deadlock. If an observer needs to dispatch into the actor, do it from a fresh goroutine (e.g. `go func() { actor.Send(EventX) }()`).
- OnInvokeCompleted fires from the invoke goroutine and does not hold the actor lock.
To implement a subset of the methods, embed BaseObserver and implement only the narrow interfaces needed.
Example ¶
ExampleObserver demonstrates the BaseObserver embedding pattern for implementing only a subset of Observer methods.
package main
import (
"context"
"fmt"
"github.com/floodfx/gstate"
)
type myState string
type myEvent string
type myCtx struct{ Count int }
func (c myCtx) Clone() myCtx {
return c
}
func buildExampleMachine() *gstate.Machine[myState, myEvent, myCtx] {
return gstate.New[myState, myEvent, myCtx]("example").
Initial("idle").
State("idle", func(s *gstate.StateBuilder[myState, myEvent, myCtx]) {
s.On("GO").
Guard(func(_ myCtx) bool { return true }).
Assign(func(c myCtx) myCtx { c.Count++; return c }).
GoTo("active")
}).
State("active", func(_ *gstate.StateBuilder[myState, myEvent, myCtx]) {}).
Build()
}
// loggingObserver embeds BaseObserver and overrides only OnTransition. It
// publishes formatted lines through a buffered channel so the example can
// observe them safely without a sleep+racy read.
type loggingObserver struct {
gstate.BaseObserver[myState, myEvent, myCtx]
lines chan string
}
func (l *loggingObserver) OnTransition(_ context.Context, e *gstate.TransitionEvent[myState, myEvent, myCtx]) {
l.lines <- fmt.Sprintf("%s --%s--> %s", e.From, e.Event, e.To)
}
func main() {
obs := &loggingObserver{lines: make(chan string, 4)}
m := buildExampleMachine()
a := gstate.Start(m, myCtx{}, m.WithObservers(obs))
defer a.Stop()
a.Send("GO")
fmt.Println(<-obs.lines)
}
Output: idle --GO--> active
func SignalObserver ¶ added in v0.2.0
SignalObserver returns an Observer whose every callback calls signal. The context and typed payload arguments are discarded — use ObserverFuncs if you need them, or embed BaseObserver for a full custom implementation.
signal must be non-blocking; observer callbacks run under the actor's write lock (see Observer's threading contract). A nil signal makes the returned observer a no-op.
Typical use: waking a channel when any lifecycle activity occurs.
ready := make(chan struct{}, 1)
obs := gstate.SignalObserver[MyState, MyEvent, MyData](func() {
select { case ready <- struct{}{}: default: }
})
actor := gstate.Start(machine, ctx, machine.WithObservers(obs))
actor.Send(EventGo)
<-ready
type ObserverFuncs ¶ added in v0.2.0
type ObserverFuncs[S ~string, E ~string, D Cloner[D]] struct { BaseObserver[S, E, D] // AnyFunc fires for every lifecycle callback before the // kind-specific field (if any). Useful as a single "something // happened" hook for waiters and counters that still want the // originating context. AnyFunc func(context.Context) TransitionFunc func(context.Context, *TransitionEvent[S, E, D]) GuardEvaluatedFunc func(context.Context, *GuardEvent[S, E, D]) InvokeStartedFunc func(context.Context, *InvokeEvent[S, E, D]) InvokeCompletedFunc func(context.Context, *InvokeEvent[S, E, D]) StateEnteredFunc func(context.Context, *StateEvent[S, E, D]) StateExitedFunc func(context.Context, *StateEvent[S, E, D]) ActionExecutedFunc func(context.Context, *ActionEvent[S, E, D]) EventReceivedFunc func(context.Context, *EventNotice[S, E, D]) EventDroppedFunc func(context.Context, *EventNotice[S, E, D]) }
ObserverFuncs is a function-field adapter that implements Observer without forcing callers to embed BaseObserver and override the callbacks they care about. Each lifecycle method dispatches first to AnyFunc (if non-nil), then to the kind-specific field (if non-nil); nil fields are no-ops.
obs := gstate.ObserverFuncs[MyState, MyEvent, MyData]{
AnyFunc: func(ctx context.Context) { /* ... */ },
TransitionFunc: func(ctx context.Context, e *gstate.TransitionEvent[MyState, MyEvent, MyData]) { /* ... */ },
}
actor := gstate.Start(machine, ctx, machine.WithObservers(obs))
ObserverFuncs values are passed by value; the implementation uses value receivers. Do not mutate fields after installing on an actor. Callback bodies must be non-blocking — see Observer's threading contract.
func (ObserverFuncs[S, E, D]) OnActionExecuted ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnActionExecuted(ctx context.Context, e *ActionEvent[S, E, D])
func (ObserverFuncs[S, E, D]) OnEventDropped ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnEventDropped(ctx context.Context, e *EventNotice[S, E, D])
func (ObserverFuncs[S, E, D]) OnEventReceived ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnEventReceived(ctx context.Context, e *EventNotice[S, E, D])
func (ObserverFuncs[S, E, D]) OnGuardEvaluated ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnGuardEvaluated(ctx context.Context, e *GuardEvent[S, E, D])
func (ObserverFuncs[S, E, D]) OnInvokeCompleted ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnInvokeCompleted(ctx context.Context, e *InvokeEvent[S, E, D])
func (ObserverFuncs[S, E, D]) OnInvokeStarted ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnInvokeStarted(ctx context.Context, e *InvokeEvent[S, E, D])
func (ObserverFuncs[S, E, D]) OnStateEntered ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnStateEntered(ctx context.Context, e *StateEvent[S, E, D])
func (ObserverFuncs[S, E, D]) OnStateExited ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnStateExited(ctx context.Context, e *StateEvent[S, E, D])
func (ObserverFuncs[S, E, D]) OnTransition ¶ added in v0.2.0
func (o ObserverFuncs[S, E, D]) OnTransition(ctx context.Context, e *TransitionEvent[S, E, D])
type Option ¶
Option configures an Actor at Start or Hydrate time. Options are constructed via methods on a typed Machine — Machine.WithMailboxSize, Machine.WithObservers, Machine.WithActorID — which lets Go infer the [S, E, C] type parameters from the machine so the call site needs no annotations:
actor := gstate.Start(m, ctx,
m.WithMailboxSize(500),
m.WithObservers(obs),
m.WithActorID("worker-42"),
)
type RecordedEvent ¶
type RecordedEvent struct {
Kind string `json:"kind"`
Payload any `json:"payload"`
Timestamp time.Time `json:"timestamp"`
}
RecordedEvent is one entry in a RecordingObserver's log. Payload holds the matching typed payload pointer (e.g. *TransitionEvent); callers can either type-assert via Payload or use the typed accessors on RecordingObserver.
func (RecordedEvent) String ¶
func (r RecordedEvent) String() string
String renders the entry as "Kind: Payload" using the payload's own String() method when it implements fmt.Stringer, falling back to %+v otherwise.
type RecordingObserver ¶
type RecordingObserver[S ~string, E ~string, D Cloner[D]] struct { BaseObserver[S, E, D] // contains filtered or unexported fields }
RecordingObserver captures every callback into an in-memory log. It is safe for concurrent use and is intended both for tests and for ad-hoc live introspection of an actor's behavior.
The recorder satisfies Observer by embedding BaseObserver and implementing all narrow interfaces; callers receive ordering identical to the engine's call sequence.
Example ¶
ExampleRecordingObserver attaches a RecordingObserver to a tiny machine, sends one event, and prints the kinds of lifecycle events recorded. A transitionBarrier composed via WithObservers synchronises the example so it doesn't need a sleep.
package main
import (
"context"
"fmt"
"github.com/floodfx/gstate"
)
type myState string
type myEvent string
type myCtx struct{ Count int }
func (c myCtx) Clone() myCtx {
return c
}
func buildExampleMachine() *gstate.Machine[myState, myEvent, myCtx] {
return gstate.New[myState, myEvent, myCtx]("example").
Initial("idle").
State("idle", func(s *gstate.StateBuilder[myState, myEvent, myCtx]) {
s.On("GO").
Guard(func(_ myCtx) bool { return true }).
Assign(func(c myCtx) myCtx { c.Count++; return c }).
GoTo("active")
}).
State("active", func(_ *gstate.StateBuilder[myState, myEvent, myCtx]) {}).
Build()
}
// transitionBarrier is a channel-backed observer used by examples to wait
// deterministically for OnTransition without time.Sleep / polling.
type transitionBarrier struct {
gstate.BaseObserver[myState, myEvent, myCtx]
done chan struct{}
}
func (b *transitionBarrier) OnTransition(_ context.Context, _ *gstate.TransitionEvent[myState, myEvent, myCtx]) {
select {
case b.done <- struct{}{}:
default:
}
}
func main() {
rec := &gstate.RecordingObserver[myState, myEvent, myCtx]{}
bar := &transitionBarrier{done: make(chan struct{}, 1)}
m := buildExampleMachine()
a := gstate.Start(m, myCtx{},
m.WithObservers(rec, bar),
)
defer a.Stop()
a.Send("GO")
<-bar.done // deterministic: blocks until OnTransition fires
for _, ev := range rec.Events() {
fmt.Println(ev.Kind)
}
}
Output: state_entered event_received guard state_exited action state_entered transition
func (*RecordingObserver[S, E, D]) Actions ¶
func (r *RecordingObserver[S, E, D]) Actions() []*ActionEvent[S, E, D]
Actions returns every recorded *ActionEvent.
func (*RecordingObserver[S, E, D]) Events ¶
func (r *RecordingObserver[S, E, D]) Events(kinds ...string) []RecordedEvent
Events returns a copy of recorded events. If kinds are supplied, only entries whose Kind matches one of them are returned, in original order.
func (*RecordingObserver[S, E, D]) EventsDropped ¶
func (r *RecordingObserver[S, E, D]) EventsDropped() []*EventNotice[S, E, D]
EventsDropped returns every recorded *EventNotice for dropped events.
func (*RecordingObserver[S, E, D]) EventsReceived ¶
func (r *RecordingObserver[S, E, D]) EventsReceived() []*EventNotice[S, E, D]
EventsReceived returns every recorded *EventNotice for received events.
func (*RecordingObserver[S, E, D]) Guards ¶
func (r *RecordingObserver[S, E, D]) Guards() []*GuardEvent[S, E, D]
Guards returns every recorded *GuardEvent.
func (*RecordingObserver[S, E, D]) InvokeCompleted ¶
func (r *RecordingObserver[S, E, D]) InvokeCompleted() []*InvokeEvent[S, E, D]
InvokeCompleted returns every recorded *InvokeEvent for invoke completions.
func (*RecordingObserver[S, E, D]) InvokeStarted ¶
func (r *RecordingObserver[S, E, D]) InvokeStarted() []*InvokeEvent[S, E, D]
InvokeStarted returns every recorded *InvokeEvent for invoke starts.
func (*RecordingObserver[S, E, D]) OnActionExecuted ¶
func (r *RecordingObserver[S, E, D]) OnActionExecuted(_ context.Context, e *ActionEvent[S, E, D])
func (*RecordingObserver[S, E, D]) OnEventDropped ¶
func (r *RecordingObserver[S, E, D]) OnEventDropped(_ context.Context, e *EventNotice[S, E, D])
func (*RecordingObserver[S, E, D]) OnEventReceived ¶
func (r *RecordingObserver[S, E, D]) OnEventReceived(_ context.Context, e *EventNotice[S, E, D])
func (*RecordingObserver[S, E, D]) OnGuardEvaluated ¶
func (r *RecordingObserver[S, E, D]) OnGuardEvaluated(_ context.Context, e *GuardEvent[S, E, D])
func (*RecordingObserver[S, E, D]) OnInvokeCompleted ¶
func (r *RecordingObserver[S, E, D]) OnInvokeCompleted(_ context.Context, e *InvokeEvent[S, E, D])
func (*RecordingObserver[S, E, D]) OnInvokeStarted ¶
func (r *RecordingObserver[S, E, D]) OnInvokeStarted(_ context.Context, e *InvokeEvent[S, E, D])
func (*RecordingObserver[S, E, D]) OnStateEntered ¶
func (r *RecordingObserver[S, E, D]) OnStateEntered(_ context.Context, e *StateEvent[S, E, D])
func (*RecordingObserver[S, E, D]) OnStateExited ¶
func (r *RecordingObserver[S, E, D]) OnStateExited(_ context.Context, e *StateEvent[S, E, D])
func (*RecordingObserver[S, E, D]) OnTransition ¶
func (r *RecordingObserver[S, E, D]) OnTransition(_ context.Context, e *TransitionEvent[S, E, D])
func (*RecordingObserver[S, E, D]) Reset ¶
func (r *RecordingObserver[S, E, D]) Reset()
Reset clears the recorded log.
func (*RecordingObserver[S, E, D]) StateEntered ¶
func (r *RecordingObserver[S, E, D]) StateEntered() []*StateEvent[S, E, D]
StateEntered returns every recorded *StateEvent for state entries.
func (*RecordingObserver[S, E, D]) StateExited ¶
func (r *RecordingObserver[S, E, D]) StateExited() []*StateEvent[S, E, D]
StateExited returns every recorded *StateEvent for state exits.
func (*RecordingObserver[S, E, D]) Transitions ¶
func (r *RecordingObserver[S, E, D]) Transitions() []*TransitionEvent[S, E, D]
Transitions returns every recorded *TransitionEvent.
type SCXMLAssign ¶
type SCXMLAssign struct {
Location string `xml:"location,attr,omitempty"`
}
SCXMLAssign represents an <assign> element.
type SCXMLDocument ¶
type SCXMLDocument struct {
XMLName xml.Name `xml:"scxml"`
XMLNS string `xml:"xmlns,attr"`
Version string `xml:"version,attr"`
Name string `xml:"name,attr"`
Initial string `xml:"initial,attr,omitempty"`
Children []SCXMLNode `xml:",any"`
}
SCXMLDocument represents the root <scxml> element.
type SCXMLInvoke ¶
SCXMLInvoke represents an <invoke> element.
func (*SCXMLInvoke) UnmarshalXML ¶
func (e *SCXMLInvoke) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error
UnmarshalXML handles <invoke> with unknown children.
type SCXMLNode ¶
type SCXMLNode struct {
Kind NodeKind `xml:"-"`
ID string `xml:"id,attr,omitempty"`
Initial string `xml:"initial,attr,omitempty"`
HistoryMode string `xml:"type,attr,omitempty"`
OnEntry *SCXMLOnEntry `xml:"onentry,omitempty"`
OnExit *SCXMLOnExit `xml:"onexit,omitempty"`
Transitions []*SCXMLTransition `xml:"transition,omitempty"`
Invoke *SCXMLInvoke `xml:"invoke,omitempty"`
Children []SCXMLNode `xml:",any"`
}
SCXMLNode represents a child element: <state>, <final>, <parallel>, or <history>. The Kind field determines the element name during marshaling.
func (SCXMLNode) IsParallel ¶
IsParallel reports whether the node is a <parallel> wrapper.
func (SCXMLNode) MarshalXML ¶
MarshalXML implements xml.Marshaler using Kind as the element name.
func (*SCXMLNode) UnmarshalXML ¶
UnmarshalXML implements xml.Unmarshaler, setting Kind from the element name.
type SCXMLOnEntry ¶
type SCXMLOnEntry struct {
XMLName xml.Name `xml:"onentry"`
SendElements []*SCXMLSend `xml:"send,omitempty"`
}
SCXMLOnEntry wraps executable content on state entry.
func (*SCXMLOnEntry) UnmarshalXML ¶
func (e *SCXMLOnEntry) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error
UnmarshalXML handles <onentry> with unknown children (raise, datamodel, etc.).
type SCXMLOnExit ¶
SCXMLOnExit wraps executable content on state exit.
func (*SCXMLOnExit) UnmarshalXML ¶
func (e *SCXMLOnExit) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error
UnmarshalXML handles <onexit> with unknown children.
type SCXMLSend ¶
type SCXMLSend struct {
Event string `xml:"event,attr,omitempty"`
Delay string `xml:"delay,attr,omitempty"`
}
SCXMLSend represents a <send> element.
type SCXMLTransition ¶
type SCXMLTransition struct {
XMLName xml.Name `xml:"transition"`
Event string `xml:"event,attr,omitempty"`
Cond string `xml:"cond,attr,omitempty"`
Target string `xml:"target,attr,omitempty"`
Assign []*SCXMLAssign `xml:"assign,omitempty"`
}
SCXMLTransition represents a <transition> element.
type Snapshot ¶
type Snapshot[S ~string, D Cloner[D]] struct { // Active lists the currently active state IDs. Active []S `json:"active"` // History maps compound state IDs to their last active child. History map[S]S `json:"history"` // Data is the deep-copied data, captured safely using [Cloner.Clone]. Data D `json:"data"` // ActorID is the stable identifier of the actor that produced this snapshot. // [Hydrate] restores it so telemetry correlation survives serialization. ActorID ActorID `json:"actor_id,omitempty"` }
Snapshot is a serializable point-in-time capture of an Actor's state.
type StateBuilder ¶
type StateBuilder[S ~string, E ~string, D Cloner[D]] struct { // contains filtered or unexported fields }
StateBuilder provides methods for configuring a specific state's properties and children.
func (*StateBuilder[S, E, D]) After ¶
func (s *StateBuilder[S, E, D]) After(d time.Duration) *TransitionBuilder[S, E, D]
After defines a transition that fires automatically after a duration.
func (*StateBuilder[S, E, D]) Always ¶
func (s *StateBuilder[S, E, D]) Always() *TransitionBuilder[S, E, D]
Always defines a transient transition that fires immediately if its guard passes.
func (*StateBuilder[S, E, D]) Entry ¶
func (s *StateBuilder[S, E, D]) Entry(fn func(D) D)
Entry adds a function to be executed when this state is entered.
func (*StateBuilder[S, E, D]) EntryLabel ¶ added in v0.2.2
func (s *StateBuilder[S, E, D]) EntryLabel(name string)
EntryLabel sets a human-readable label for the state's Entry actions. Used in Mermaid output to identify what runs on entry.
func (*StateBuilder[S, E, D]) Exit ¶
func (s *StateBuilder[S, E, D]) Exit(fn func(D) D)
Exit adds a function to be executed when this state is left.
func (*StateBuilder[S, E, D]) ExitLabel ¶ added in v0.2.2
func (s *StateBuilder[S, E, D]) ExitLabel(name string)
ExitLabel sets a human-readable label for the state's Exit actions. Used in Mermaid output to identify what runs on exit.
func (*StateBuilder[S, E, D]) History ¶
func (s *StateBuilder[S, E, D]) History(t HistoryType)
History enables history tracking for this state.
func (*StateBuilder[S, E, D]) Initial ¶
func (s *StateBuilder[S, E, D]) Initial(id S)
Initial sets the default child state to enter for this compound state.
func (*StateBuilder[S, E, D]) Invoke ¶
func (s *StateBuilder[S, E, D]) Invoke(fn func(ctx context.Context, snap D, mutate func(func(D) D)) error, onDone S, onError S)
Invoke configures an asynchronous service to run during the state's lifecycle.
Parameters:
- fn: service function receiving ctx, entry snapshot, and mutate callback. For details on the parameter contracts, see the documentation for InvokeDef.Func.
- onDone: state to transition to on success (when fn returns nil).
- onError: state to transition to on failure (when fn returns a non-nil error).
func (*StateBuilder[S, E, D]) InvokeLabel ¶ added in v0.2.2
func (s *StateBuilder[S, E, D]) InvokeLabel(name string)
InvokeLabel sets a human-readable label for the state's invoked service. Used in Mermaid output (e.g. as the diamond pseudo-state label). No-op if Invoke has not been called yet.
func (*StateBuilder[S, E, D]) On ¶
func (s *StateBuilder[S, E, D]) On(event E) *TransitionBuilder[S, E, D]
On defines a transition triggered by a specific event.
func (*StateBuilder[S, E, D]) State ¶
func (s *StateBuilder[S, E, D]) State(id S, fn func(*StateBuilder[S, E, D]))
State defines a nested child state.
func (*StateBuilder[S, E, D]) Type ¶
func (s *StateBuilder[S, E, D]) Type(t StateType)
Type explicitly sets the StateType (e.g., Parallel or Final).
type StateDef ¶
type StateDef[S ~string, E ~string, D Cloner[D]] struct { // ID uniquely identifies this state within the machine. ID S // Type is the kind of state: Atomic, Compound, Parallel, or Final. Type StateType // Initial is the default child state to enter for Compound states. Initial S // States maps child state IDs to their definitions. States map[S]*StateDef[S, E, D] // Transitions maps event IDs to ordered transition definitions. // The first transition whose guard passes (or has no guard) fires. Transitions map[E][]*TransitionDef[S, E, D] // Always holds eventless transitions evaluated on state entry in declaration order. Always []*TransitionDef[S, E, D] // Delayed holds time-based transitions that fire after their After duration. Delayed []*TransitionDef[S, E, D] // Entry holds functions called in order when entering this state. Entry []func(D) D // Exit holds functions called in order when leaving this state. Exit []func(D) D // Invoke defines an async service started on entry and cancelled on exit. Invoke *InvokeDef[S, E, D] // History controls history tracking: None, Shallow, or Deep. History HistoryType // contains filtered or unexported fields }
StateDef defines the properties and behavior of a single state in the machine.
type StateEnteredObserver ¶ added in v0.3.0
type StateEvent ¶
type StateEvent[S ~string, E ~string, D Cloner[D]] struct { MachineID string `json:"machine_id"` ActorID ActorID `json:"actor_id"` State S `json:"state"` Timestamp time.Time `json:"timestamp"` // contains filtered or unexported fields }
StateEvent is the payload for [Observer.OnStateEntered] and [Observer.OnStateExited].
func (*StateEvent[S, E, D]) Data ¶ added in v0.3.0
func (e *StateEvent[S, E, D]) Data() *D
func (*StateEvent[S, E, D]) MarshalJSON ¶ added in v0.3.0
func (e *StateEvent[S, E, D]) MarshalJSON() ([]byte, error)
func (*StateEvent[S, E, D]) String ¶
func (e *StateEvent[S, E, D]) String() string
String renders the state event as "state[ActorID]: State".
type StateExitedObserver ¶ added in v0.3.0
type StateType ¶
type StateType int
StateType represents the type of a state in the statechart.
const ( // Atomic represents a leaf state with no children. Atomic StateType = iota // Compound represents a state that contains one or more child states. // It has exactly one active child at any given time. Compound // Parallel represents a state where all of its child regions are active simultaneously. Parallel // Final represents a state that indicates the completion of its parent's // process. When the actor's top-level active state has reached "done" // (an atomic Final, a compound whose active child is done, or a parallel // whose every region is done), the actor stops itself automatically. // See [Actor.Stop] for the shutdown contract. // // Machines that contain no Final state never auto-stop; their actors run // until [Actor.Stop] is called explicitly. Final )
type TransitionBuilder ¶
type TransitionBuilder[S ~string, E ~string, D Cloner[D]] struct { // contains filtered or unexported fields }
TransitionBuilder provides a fluent API for configuring a state transition.
func (*TransitionBuilder[S, E, D]) ActionLabel ¶
func (t *TransitionBuilder[S, E, D]) ActionLabel(name string) *TransitionBuilder[S, E, D]
ActionLabel sets an optional label for the action.
func (*TransitionBuilder[S, E, D]) Assign ¶
func (t *TransitionBuilder[S, E, D]) Assign(fn func(D) D) *TransitionBuilder[S, E, D]
Assign adds a data update action to the transition.
func (*TransitionBuilder[S, E, D]) GoTo ¶
func (t *TransitionBuilder[S, E, D]) GoTo(target S)
GoTo sets the target state for the transition.
func (*TransitionBuilder[S, E, D]) Guard ¶
func (t *TransitionBuilder[S, E, D]) Guard(fn func(D) bool) *TransitionBuilder[S, E, D]
Guard adds a conditional check to the transition.
func (*TransitionBuilder[S, E, D]) GuardLabel ¶
func (t *TransitionBuilder[S, E, D]) GuardLabel(name string) *TransitionBuilder[S, E, D]
GuardLabel sets an optional label for the guard condition.
type TransitionDef ¶
type TransitionDef[S ~string, E ~string, D Cloner[D]] struct { // Target is the state to transition to. Target S // Guard is an optional predicate that must return true for the transition to fire. Guard func(D) bool // Action is a pure function that updates the data during the transition. Action func(D) D // After is the delay before a timed transition fires. After time.Duration // contains filtered or unexported fields }
TransitionDef defines the rules for moving from one state to another.
type TransitionEvent ¶
type TransitionEvent[S ~string, E ~string, D Cloner[D]] struct { MachineID string `json:"machine_id"` ActorID ActorID `json:"actor_id"` From S `json:"from"` To S `json:"to"` // Event is the triggering event. Zero value when the transition fires from // an Always, Delayed, or invoke-completion path. Event E `json:"event,omitempty"` Timestamp time.Time `json:"timestamp"` // contains filtered or unexported fields }
TransitionEvent is the payload for [Observer.OnTransition].
func (*TransitionEvent[S, E, D]) Data ¶ added in v0.3.0
func (e *TransitionEvent[S, E, D]) Data() *D
func (*TransitionEvent[S, E, D]) MarshalJSON ¶ added in v0.3.0
func (e *TransitionEvent[S, E, D]) MarshalJSON() ([]byte, error)
func (*TransitionEvent[S, E, D]) String ¶
func (e *TransitionEvent[S, E, D]) String() string
String renders the transition as "transition[ActorID]: From --Event--> To". To is "<internal>" for transitions without a target state.
type TransitionObserver ¶ added in v0.3.0
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
agent
command
|
|
|
basics
command
|
|
|
delayed
command
|
|
|
hierarchy
command
|
|
|
history
command
|
|
|
invoke
command
|
|
|
observer
command
Package main demonstrates attaching an Observer to a gstate machine to inspect lifecycle events end-to-end.
|
Package main demonstrates attaching an Observer to a gstate machine to inspect lifecycle events end-to-end. |
|
parallel
command
|
|
|
persistence
command
|
|
|
internal
|
|
|
mermaid
Package mermaid emits Mermaid flowchart diagram source strings.
|
Package mermaid emits Mermaid flowchart diagram source strings. |