core

package
v2.0.0+incompatible Latest Latest
Warning

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

Go to latest
Published: Apr 15, 2019 License: Apache-2.0 Imports: 11 Imported by: 10

Documentation

Overview

Package core provides the core gear for specification-driven message processing. These specifications are structured as state transitions rules based on pattern matching against either a pending message or the state's current Bindings.

The primary type is Spec(ification), and the primary method is Walk(). A Spec specifies how to transition from one State to another State. A State is a node name (a string) and a set of Bindings (a map[string]interface{}).

A Spec can use arbitrary code for actions (and guards). When a Spec is Compiled, the compiler looks for ActionSources, each of which should specify an Interpreter. An Interpreter should know how to Compile and Exec an ActionSource. Alternately, a native Spec can provide an ActionFunc implemented in Go.

Ideally an Action does not block or perform any IO. Instead, an Action returns a structure that includes updated Bindings and zero or more messages to emit. (This structure can also include tracing and diagnostic messages.) Therefore, an action should have no side effects.

In order for an action to influence the world in some way, the package user must do something with messages that actions emit. For example, an application could provide a mechanism for certain messages to result in HTTP requests. (Such an HTTP request would then result in the subsequent response (or error) to be forwarded as a message for further processing.)

To use this package, make a Spec. Then Compile() it. You might also want to Analyze() it. Then, given an initial State an a sequence of messages, you can Walk() to the next State.

See https://github.com/Comcast/sheens for an overview.

Example

Example demonstrates Walk()ing.

type note string

spec := &Spec{
	Name:          "test",
	PatternSyntax: "json",
	Nodes: map[string]*Node{
		"start": {
			Branches: &Branches{
				Type: "message",
				Branches: []*Branch{
					{
						Pattern: `{"request":"?something"}`,
						Target:  "obey",
					},
					{
						Pattern: `{"gimme":"?something"}`,
						Target:  "ignore",
					},
				},
			},
		},
		"obey": {
			Action: &FuncAction{
				F: func(ctx context.Context, bs Bindings, props StepProps) (*Execution, error) {
					e := NewExecution(make(Bindings)) // Forget current bindings.
					e.Events.AddEmitted(bs["?something"])
					e.Events.AddTrace(note("polite"))
					return e, nil
				},
			},
			Branches: &Branches{
				Branches: []*Branch{
					{
						Target: "start",
					},
				},
			},
		},
		"ignore": {
			Action: &FuncAction{
				F: func(ctx context.Context, bs Bindings, props StepProps) (*Execution, error) {
					e := NewExecution(make(Bindings)) // Forget current bindings.
					e.Events.AddTrace(note("rude"))
					return e, nil
				},
			},
			Branches: &Branches{
				Branches: []*Branch{
					{
						Target: "start",
					},
				},
			},
		},
	},
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

if err := spec.Compile(ctx, nil, true); err != nil {
	panic(err)
}

st := &State{
	NodeName: "start",
	Bs:       make(Bindings),
}

ctl := &Control{
	Limit: 10,
}

messages := []interface{}{
	Dwimjs(`{"gimme":"queso"}`),
	Dwimjs(`{"request":"chips"}`),
}

walked, _ := spec.Walk(ctx, st, messages, ctl, nil)
for i, stride := range walked.Strides {
	if stride.To != nil {
		fmt.Printf("%02d stride % -32s → % -32s consumed: %s\n",
			i, stride.From, stride.To, JS(stride.Consumed))
	} else {
		fmt.Printf("%02d stride % -32s (no movement)\n",
			i, stride.From)
	}
	for _, m := range stride.Events.Emitted {
		fmt.Printf("   emit   %s\n", JS(m))
	}
	for _, m := range stride.Events.Traces.Messages {
		switch m.(type) {
		case note:
			fmt.Printf("   note   %s\n", JS(m))
		}
	}
}
Output:

00 stride start/{}                         → ignore/{"?something":"queso"}    consumed: {"gimme":"queso"}
01 stride ignore/{"?something":"queso"}    → start/{}                         consumed: null
   note   "rude"
02 stride start/{}                         → obey/{"?something":"chips"}      consumed: {"request":"chips"}
03 stride obey/{"?something":"chips"}      → start/{}                         consumed: null
   emit   "chips"
   note   "polite"
04 stride start/{}                         (no movement)

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// InterpreterNotFound occurs when you try to Compile an
	// ActionSource, and the required interpreter isn't in the
	// given map of interpreters.
	InterpreterNotFound = errors.New("interpreter not found")

	// DefaultInterpreters will be used in ActionSource.Compile if
	// the given nil interpreters.
	DefaultInterpreters = InterpretersMap{}

	// Exp_PermanentBindings is a switch to enable an experiment
	// that makes a binding key ending in "!" a permament binding
	// in the sense that an Action cannot remove that binding.
	//
	// The implementation has overhead for gathering the permanent
	// bindings before the Action execution and restoring those
	// bindings after the execution.
	//
	// One motivation for this feature is enabling an updated Spec
	// to be asked to do something along the lines of migration.
	// The old Spec version can be remembered as a permanent
	// binding.  When that Spec is updated, we can take some
	// action.
	//
	// Another motivation is simply the persistence (so to speak)
	// of configuration-like bindings, which we do not want to
	// remove accidentally.  With this experiment enabled, an
	// Action can call for removing all bindings, but the result
	// will still include the permanent bindings.  Can make
	// writing Actions easier and safer.
	Exp_PermanentBindings = true
)
View Source
var (
	// DefaultPatternParser is used during Spec.Compile if the
	// given Spec has no PatternParser.
	//
	// This function is useful to allow a Spec to provide branch
	// patterns in whatever syntax is convenient.  For example, if
	// a Spec is authored in YAML, patterns in JSON might be more
	// convenient (or easier to read) that patterns in YAML.
	DefaultPatternParser = func(syntax string, p interface{}) (interface{}, error) {
		switch syntax {
		case "none", "":
			return p, nil
		case "json":
			if js, is := p.(string); is {
				var x interface{}
				if err := json.Unmarshal([]byte(js), &x); err != nil {
					return nil, err
				}
				return x, nil
			}
			return p, nil
		default:
			return nil, errors.New("unsupposed pattern syntax: " + syntax)
		}
	}

	// DefaultBranchType is used for Branches.Type when
	// Branches.Type is zero.  This var should probably be a
	// const.
	DefaultBranchType = "bindings"
)
View Source
var (
	// TracesInitialCap is the initial capacity for Traces buffers.
	TracesInitialCap = 16

	// EmittedMessagesInitialCap is the initial capacity for
	// slices of emitted messages.
	EmittedMessagesInitialCap = 16

	// DefaultControl will be used by Spec.Step (and therefore
	// Spec.Walk) if the given control is nil.
	DefaultControl = &Control{
		Limit: 100,
	}

	// Exp_BranchTargetVariables is a switch that enables a branch
	// target to be a reference to a binding.  If a branch target
	// is of the form "@VAR", then the current binding for VAR (if
	// any) is used as the branch target.  If anything goes wrong,
	// the branch target is returned as the literal value of the
	// branch's Target.
	//
	// This feature should be used sparingly if at all.  The
	// motivating use was for a Spec.Boot or a "boot" node/Action,
	// which could be used to state migration when a Spec is
	// updated.  Being able to have a branch target that is passed
	// via bindings would make it much easier to write an Action
	// that can reset a machine based on the machine's previous
	// node.
	Exp_BranchTargetVariables = true
)
View Source
var TooManyBindingss = errors.New("too many bindingss")

TooManyBindingss occurs when a guard returns more than one set of bindings.

Functions

func Canonicalize

func Canonicalize(x interface{}) (interface{}, error)

Canonicalize is ... hey, look over there!

func Gensym

func Gensym(n int) string

Gensym makes a random string of the given length.

Since we're returning a string and not (somehow a symbol), should be named something else. Using this name just brings back good memories.

func IsBranchTargetVariable

func IsBranchTargetVariable(s string) bool

func Timestamp

func Timestamp() string

Timestamp returns a string representing the current time in RFC3339Nano.

func Unquestion

func Unquestion(p string) string

Unquestion removes (so to speak) a leading question mark (if any).

Types

type Action

type Action interface {
	// Exec executes this action.
	//
	// Third argument is for parameters (which can be exposed in
	// the Action's dynamic environment).
	//
	// ToDo: Generalize to return []Bindings?
	Exec(context.Context, Bindings, StepProps) (*Execution, error)

	// Binds optionally gives the set of patterns that match
	// bindings that could be bound during execution.
	//
	// If not nil, the returned set can help with static and
	// dynamic analysis of the machine.
	Binds() []Bindings

	// Emits optionally gives the set of patterns that match
	// messages that could be emitted by this action.
	Emits() []interface{}
}

Action returns Bindings based on the given (current) Bindings.

type ActionSource

type ActionSource struct {
	Interpreter string      `json:"interpreter,omitempty" yaml:",omitempty"`
	Source      interface{} `json:"source"`
	Binds       []Bindings  `json:"binds,omitempty" yaml:",omitempty"`
}

ActionSource can be compiled to an Action.

func (*ActionSource) Compile

func (a *ActionSource) Compile(ctx context.Context, interpreters Interpreters) (Action, error)

Compile attempts to compile the ActionSource into an Action using the given interpreters, which defaults to DefaultInterpreters.

func (*ActionSource) Copy

func (a *ActionSource) Copy() *ActionSource

Copy makes a shallow copy.

Needed for Specification.Copy().

type BadBranching

type BadBranching struct {
	Spec     *Spec
	NodeName string
}

BadBranching occurs when somebody the a Spec.Branches isn't right.

For example, a Branch with an action must have braching type "message". If not, you'll get an BadBranching error.

func (*BadBranching) Error

func (e *BadBranching) Error() string

type Branch

type Branch struct {
	// Pattern is matched against either a pending message or
	// bindings -- depending on the Branches.Type.
	Pattern interface{} `json:"pattern,omitempty" yaml:",omitempty"`

	// Guard is an optional procedure that will prevent the
	// transition if the procedure returns nil Bindings.
	Guard Action `json:"-" yaml:"-"`

	// GuardSource is an ActionSource that serves as a guard to
	// following this branch.  If the guard returns nil bindings,
	// the branch isn't followed.  Otherwise, the returns bindings
	// are used and the branch is followed.
	GuardSource *ActionSource `json:"guard,omitempty" yaml:"guard,omitempty"`

	// Target is the name of the next state for this transition.
	Target string `json:"target,omitempty" yaml:",omitempty"`
}

Branch is a possible transition to the next state.

func (*Branch) Copy

func (b *Branch) Copy() *Branch

Copy makes a shallow copy of the Branch.

type Branches

type Branches struct {
	// Type is either "message", "bindings", or nil.
	//
	// Type "message" means that an message is required and will be
	// consumed when branches are considered.  Branch Patterns are
	// matched against that message.
	//
	// Type "bindings" means that Branch Patterns are matched
	// against the current Bindings.
	//
	// A nil Type should imply only one Branch with no Pattern.
	Type string `json:"type,omitempty" yaml:",omitempty"`

	// Modes is a set of flags that can inform Branch processing
	// and analysis.  Currently no modes are considered.
	//
	// Example: "exclusive" might declare that the Branch patterns
	// should be mututally exclusive.
	//
	// ToDo: Use a real type instead of string.
	Modes []string `json:"modes,omitempty" yaml:",omitempty"`

	// Branches is the list (ordered) of possible transitions to
	// the next state (if any).
	//
	// No Branches means that this node is terminal.
	Branches []*Branch `json:"branches,omitempty" yaml:",omitempty"`
}

Branches represents the possible transitions to next states.

func (*Branches) Copy

func (b *Branches) Copy() *Branches

Copy makes a deep copy of the Branches.

type Breakpoint

type Breakpoint func(context.Context, *State) bool

Breakpoint is a *State predicate.

When a Breakpoint returns true for a *State, then processing should stop at that point.

type Context

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

Context is a copy of context.Context.

This type is defined here in order to avoid importing context.Context, which pulls with it a ton of other stuff (e.g. fmt). See https://github.com/Comcast/sheens/issues/13 and 14.

type Control

type Control struct {
	// Limit is the maximum number of Steps that a Walk() can take.
	Limit       int                   `json:"limit"`
	Breakpoints map[string]Breakpoint `json:"-"`
}

Control influences how Walk() operates.

func (*Control) Copy

func (c *Control) Copy() *Control

type Events

type Events struct {
	Emitted []interface{} `json:"emitted,omitempty" yaml:",omitempty"`
	Traces  *Traces       `json:"traces,omitempty" yaml:",omitempty"`
}

Events contains emitted messages and Traces.

func (*Events) AddEmitted

func (es *Events) AddEmitted(x interface{})

AddEmitted adds the given thing to the list of emitted messages.

func (*Events) AddEvents

func (es *Events) AddEvents(more *Events)

AddEvents adds the given Event's emitted messages and traces to the receiving Events.

func (*Events) AddTrace

func (es *Events) AddTrace(x interface{})

AddTrace adds the given thing to the list of traces.

type Execution

type Execution struct {
	Bs Bindings
	*Events
}

func NewExecution

func NewExecution(bs Bindings) *Execution

type FuncAction

type FuncAction struct {
	F func(context.Context, Bindings, StepProps) (*Execution, error) `json:"-" yaml:"-"`
	// contains filtered or unexported fields
}

FuncAction is currently a wrapper around a Go function, but an Action will eventually be a specification for generating an outbound message.

func (*FuncAction) Binds

func (a *FuncAction) Binds() []Bindings

func (*FuncAction) Emits

func (a *FuncAction) Emits() []interface{}

func (*FuncAction) Exec

func (a *FuncAction) Exec(ctx context.Context, bs Bindings, props StepProps) (*Execution, error)

Exec runs the given action.

type Interpreter

type Interpreter interface {
	// Compile can make something that helps when Exec()ing the
	// code later.
	Compile(ctx context.Context, code interface{}) (interface{}, error)

	// Exec executes the code.  The result of previous Compile()
	// might be provided.
	Exec(ctx context.Context, bs Bindings, props StepProps, code interface{}, compiled interface{}) (*Execution, error)
}

Interpreter can optionally compile and execute code for Actions and guards.

type Interpreters

type Interpreters interface {
	Find(interpreterName string) Interpreter
}

Interpreters resolves an interpreter name (like "ecmascript") to an Interpreter.

See InterpretersMap for a simple implementation.

type InterpretersMap

type InterpretersMap map[string]Interpreter

InterpretersMap is a simple implementation of Interpreters.

func NewInterpretersMap

func NewInterpretersMap() InterpretersMap

func (InterpretersMap) Find

func (m InterpretersMap) Find(name string) Interpreter

type Node

type Node struct {
	// Doc is optional document in a format of your choosing.
	Doc string `json:"doc,omitempty" yaml:",omitempty"`

	// Action is an optional action that will be executed upon
	// transition to this node.
	//
	// Note that a node with "message"-based branching cannot have
	// an Action.
	Action Action `json:"-" yaml:"-"`

	// ActionSource, if given, is Compile()ed to an Action.
	ActionSource *ActionSource `json:"action,omitempty" yaml:"action,omitempty"`

	// Branches contains the transitions out of this node.
	Branches *Branches `json:"branching,omitempty" yaml:"branching,omitempty"`
}

Node represents the structure of something like a state in a state machine.

In our machines, the state is really given by (1) the name of the current node (At) and (2) the current Bindings. A Node given a optional Action and possible state transitions.

func (*Node) Copy

func (n *Node) Copy() *Node

Copy makes a deep copy of the Node.

func (*Node) Terminal

func (n *Node) Terminal() bool

Terminal determines if a node has no branches.

type ParamSpec

type ParamSpec struct {

	// Doc describes the spec in English and Markdown.  Audience
	// is developers, not users.
	Doc string `json:"doc,omitempty" yaml:",omitempty"`

	// PrimitiveType is (for now) any string.
	PrimitiveType string `json:"primitiveType" yaml:"primitiveType"`

	// Default is the default value for a parameter used in case a value is not given in the initial bindings
	Default interface{} `json:"default"`

	// Optional means that the parameter is not required!
	Optional bool `json:"optional,omitempty" yaml:",omitempty"`

	// IsArray specifies whether a value must be an array of
	// values of the PrimitiveType.
	IsArray bool `json:"isArray,omitempty" yaml:"isArray,omitempty"`

	// SemanticType is (for now) any string.
	SemanticType string `json:"semanticType,omitempty" yaml:"semanticType,omitempty"`

	// MinCardinality is the minimum number of values.
	MinCardinality int `json:"minCard,omitempty" yaml:"minCard,omitempty"`

	// MaxCardinality is the maximum number of values.
	//
	// A MaxCardinality greater than one implies the param value
	// IsArray.
	MaxCardinality int `json:"maxCard,omitempty" yaml:"maxCard,omitempty"`

	// Predicate isn't implemented.
	//
	// Could be a predicate that could further validate a
	// parameter value.
	Predicate interface{} `json:"predicate,omitempty" yaml:",omitempty"`

	// Advisory indicates that a violation of this spec is a
	// warning, not an error.
	Advisory bool `json:"advisory,omitempty" yaml:",omitempty"`
}

ParamSpec is a strawman struct to represent data about required and optional Machine parameters (which are just initial bindings).

Probably would be better to start with an interface, but then deserialization is more trouble.

We're not over-thinking this struct right now. Really just a strawman. Should be much better.

func (*ParamSpec) Valid

func (s *ParamSpec) Valid() error

Valid should return an error if the given spec is bad for some reason.

Currently just returns nil.

Probably shouldn't return an error, but we'll just go with that for now.

func (*ParamSpec) ValueCompilesWith

func (s *ParamSpec) ValueCompilesWith(x interface{}) error

ValueCompilesWith checks that the given value complies with the spec. Returns an error if not.

Currently just returns nil.

Probably shouldn't return an error, but we'll just go with that for now.

type Spec

type Spec struct {
	// Name is the generic name for this machine.  Something like
	// "door-open-notification".  Cf. Id.
	Name string `json:"name,omitempty" yaml:",omitempty"`

	// Version is the version of this generic machine.  Something
	// like "1.2".
	Version string `json:"version,omitempty" yaml:",omitempty"`

	// Id should be a globally unique identifier (such as a hash
	// of a canonical representation of the Spec).
	//
	// This value could be used to determine when a Spec has
	// changed.
	//
	// This package does not read or write this value.
	Id string `json:"id,omitempty" yaml:",omitempty"`

	// Doc is general documentation about how this specification works.
	Doc string `json:"doc,omitempty" yaml:",omitempty"`

	// ParamSpecs is an optional name from a parameter name to a
	// specification for that parameter.
	//
	// A parameter is really just an initial binding that's
	// provided when a machine is created.
	//
	// ToDo: Implement actual check of parameters when machine is
	// created.
	ParamSpecs map[string]ParamSpec `json:"paramSpecs,omitempty" yaml:",omitempty"`

	// Uses is a set of feature tags.
	Uses []string `json:"uses,omitempty" yaml:",omitempty"`

	// Nodes is the structure of the machine.  This value could be
	// a reference that points into a library or whatever.
	Nodes map[string]*Node `json:"nodes,omitempty" yaml:",omitempty"`

	// ErrorNode is an optional name of a node for the machine in
	// the even of an internal error.
	//
	// Probably should just always assume the convention that a
	// node named 'error' is the error node.  ToDo: Consider.
	ErrorNode string `json:"errorNode,omitempty" yaml:",omitempty"`

	// NoAutoErrorNode will instruct the spec compiler not to add
	// an error node if one does not already exist.
	NoAutoErrorNode bool `json:"noErrorNode,omitempty" yaml:",omitempty"`

	// ActionErrorBranches (when true) means that this spec uses
	// branches to handle action errors.  (A branch can match an
	// action error using a "actionError" property with a variable
	// value.)
	//
	// If this switch is off, then any action error will result in
	// a transition to the error state, which is probably not what
	// you want.
	ActionErrorBranches bool `json:"actionErrorBranches,omitempty" yaml:",omitempty"`

	// ActionErrorNode is the name of the target node when an
	// action produces an error.
	//
	// If no value is given, then Step() will return an error
	// rather than a stride ending at a node given by this value.
	ActionErrorNode string `json:"actionErrorNode,omitempty" yaml:",omitempty"`

	// Boot is an optional Action that should be (?) executed when
	// the machine is loaded.  Not implemented yet.
	Boot Action `json:"-" yaml:"-"`

	// BootSource, if given, can be compiled to a Boot Action.
	// See Spec.Compile.
	BootSource *ActionSource `json:"boot,omitempty" yaml:"boot,omitempty"`

	// Toob is of course Boot in reverse.  It's also an optional
	// Action that can/should be executed when a Machine is
	// unloaded, suspended, or whatever.  Not currently connected
	// to anything.
	Toob Action `json:"-" yaml:"-"`

	// ToobSource, if given, can be compiled to a Toob Action.
	// See Spec.Compile.
	ToobSource *ActionSource `json:"toob,omitempty" yaml:"toob,omitempty"`

	// PatternSyntax indicates the syntax (if any) for branch patterns.
	PatternSyntax string `json:"patternSyntax,omitempty" yaml:",omitempty"`

	PatternParser func(string, interface{}) (interface{}, error) `json:"-" yaml:"-"`

	// NoNewMachines will make Step return an error if a pattern
	// match returns more than one set of bindings.
	//
	// ToDo: Implement.
	NoNewMachines bool `json:"noNewMachined,omitempty" yaml:",omitempty"`
	// contains filtered or unexported fields
}

Spec is a specification used to build a machine.

A specification gives the structure of the machine. This data does not include any state (such as the name of the current Node or a Machine's Bindings).

If a specification includes Nodes with ActionSources, then the specification should be Compiled before use.

func TurnstileSpec

func TurnstileSpec(ctx context.Context) (*Spec, error)

TurnstileSpec makes an example Spec that's useful to have around.

See https://en.wikipedia.org/wiki/Finite-state_machine#Example:_coin-operated_turnstile.

func (*Spec) Compile

func (spec *Spec) Compile(ctx context.Context, interpreters Interpreters, force bool) error

Compile compiles all action-like sources into actions. Might also do some other things.

Action-like sources include Actions, Boot, Toob, and Guards.

func (*Spec) Copy

func (spec *Spec) Copy(version string) *Spec

Copy makes a deep copy of the Spec.

func (*Spec) ParsePatterns

func (spec *Spec) ParsePatterns(ctx context.Context) error

ParsePatterns parses branch patterns.

The method Compile calls this method. ParsePatterns is exposed to tools that might need to parse patterns without wanted to Compile them.

func (*Spec) Spec

func (s *Spec) Spec() *Spec

Spec makes any Spec a Specter.

func (*Spec) Step

func (s *Spec) Step(ctx context.Context, st *State, pending interface{}, c *Control, props StepProps) (*Stride, error)

Step is the fundamental operation that attempts to move from the given state.

The given pending message (if any) will be consumed by "message" type Branches.

func (*Spec) Walk

func (s *Spec) Walk(ctx context.Context, st *State, pendings []interface{}, c *Control, props StepProps) (*Walked, error)

Walk takes as many steps as it can.

Any returned error is an internal error. Almost all errors encountered during processing should transition to the "error" node with a binding for "error".

Any unprocessed messages are returned. This method should only returned some unprocessed messages if the method encountered an internal error.

type SpecNotCompiled

type SpecNotCompiled struct {
	Spec *Spec
}

SpecNotCompiled occurs when a Spec is used (say via Step()) before it has been Compile()ed.

func (*SpecNotCompiled) Error

func (e *SpecNotCompiled) Error() string

type Specter

type Specter interface {
	Spec() *Spec
}

Specter enables other things to manifest themselves as Specs.

Specters can be spooky.

A Spec is itself a Specter. An UpdatableSpec is also a Specter, but it's not itself Spec.

Specter is not used anywhere in this package. It's defined here for convenience and encouragement.

type State

type State struct {
	NodeName string   `json:"node"`
	Bs       Bindings `json:"bs"`
}

State represents the current state of a machine given a specification.

func (*State) Copy

func (s *State) Copy() *State

Copy makes a deep copy of the State.

func (*State) String

func (s *State) String() string

type StepProps

type StepProps map[string]interface{}

func (StepProps) Copy

func (ps StepProps) Copy() StepProps

type StopReason

type StopReason int

StopReason represents the possible reasons for a Walk to terminate.

const (
	Done              StopReason = iota // Went as far as the Spec allowed.
	Limited                             // Too many steps.
	InternalError                       // What else to do?
	BreakpointReached                   // During a Walk.
)

func (StopReason) MarshalJSON

func (r StopReason) MarshalJSON() ([]byte, error)

MarshalJSON is generated so StopReason satisfies json.Marshaler.

func (StopReason) String

func (i StopReason) String() string

func (*StopReason) UnmarshalJSON

func (r *StopReason) UnmarshalJSON(data []byte) error

UnmarshalJSON is generated so StopReason satisfies json.Unmarshaler.

type Stride

type Stride struct {
	// Events gather what was emitted during the step.
	*Events `json:"events,omitempty" yaml:",omitempty"`

	// From is the name of the starting node.
	From *State `json:"from,omitempty" yaml:",omitempty"`

	// To is the new State (if any) resulting from the step.
	To *State `json:"to,omitempty" yaml:",omitempty"`

	// Consumed is the message (if any) that was consumed by the step.
	Consumed interface{} `json:"consumed,omitempty" yaml:",omitempty"`
}

Stride represents a step that Walk has taken or attempted.

func NewStride

func NewStride() *Stride

type Traces

type Traces struct {
	Messages []interface{} `json:"messages,omitempty" yaml:",omitempty"`
}

Traces holds trace messages.

func NewTraces

func NewTraces() *Traces

NewTraces creates an initialized Traces.

The Messages array has TracesSize initial capacity.

func (*Traces) Add

func (ts *Traces) Add(xs ...interface{})

type UncompiledAction

type UncompiledAction struct {
	Spec     *Spec
	NodeName string
}

UncompiledAction occurs when an ActionSource execution is attempted but that ActionSource hasn't been Compile()ed. Usually, this compilation happens as part of Spec.Compile().

func (*UncompiledAction) Error

func (e *UncompiledAction) Error() string

type UnknownNode

type UnknownNode struct {
	Spec     *Spec
	NodeName string
}

UnknownNode occurs when a branch is followed and its target node is not in the Spec.

func (*UnknownNode) Error

func (e *UnknownNode) Error() string

type UpdatableSpec

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

UpdatableSpec is a scary yet handy Specter with an underlying Spec that can be changed at any time.

This capability motivated Specters.

func NewUpdatableSpec

func NewUpdatableSpec(spec *Spec) *UpdatableSpec

NewUpdatableSpec makes one with the given initial spec, which can be changed later via SetSpec.

func (*UpdatableSpec) SetSpec

func (s *UpdatableSpec) SetSpec(spec *Spec) error

SetSpec atomically changes the underlying spec.

func (*UpdatableSpec) Spec

func (s *UpdatableSpec) Spec() *Spec

Spec implements the Specter interface.

type Walked

type Walked struct {
	// Strides contains each Stride taken and the last one
	// attempted.
	Strides []*Stride `json:"strides" yaml:",omitempty"`

	// Remaining stores the messages that Walk failed to consume.
	Remaining []interface{} `json:"remaining,omitempty" yaml:",omitempty"`

	// StoppedBecause reports the reason why the Walk stopped.
	StoppedBecause StopReason `json:"stoppedBecause,omitempty" yaml:",omitempty"`

	// Error stores an internal error that occurred (if any).
	Error error `json:"error,omitempty" yaml:",omitempty"`

	// BreakpointId is the id of the breakpoint, if any, that
	// caused this Walk to stop.
	BreakpointId string `json:"breakpoint,omitempty" yaml:",omitempty"`
}

Walked represents a sequence of strides taken by a Walk().

func (Walked) DoEmitted

func (w Walked) DoEmitted(f func(x interface{}) error) error

DoEmitted is a convenience method to iterate over messages emitted by the Walked.

func (*Walked) From

func (w *Walked) From() *State

func (*Walked) To

func (w *Walked) To() *State

Jump to

Keyboard shortcuts

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