macro_rules! StateMachineDefinition {
(
for $standin:ty;
pub Initial: $first_initial:ident $(| $initial:ident)*;
$($transitions:tt)*
) => { ... };
(
for $standin:ty;
Initial: $first_initial:ident $(| $initial:ident)*;
$($transitions:tt)*
) => { ... };
(
for $standin:ty;
$($transitions:tt)*
) => { ... };
}Expand description
Defines the state-machine contract for a stand-in type.
Use this macro in the crate that owns the state marker types and the
stand-in ZST. The generated code contains only contract information:
Initial implementations, Transition implementations, and optional
state-union declarations. It deliberately contains no runtime type and no
transition bodies. Think of this as the public state-machine interface: it
says which states exist, which states may be constructed initially, and
which edges are legal. If no state should be constructible from raw runtime
data, omit the pub Initial: line entirely; values can still enter the
machine through target-owned constructors that build a
ConcreteStated and pass it to
State::from_concrete.
That separation is the main enforcement mechanism. A downstream crate can implement behavior for its runtime type, but it cannot add new legal transitions unless it owns the stand-in or the state markers. In the normal split-crate layout, the definition crate owns both, so Rust’s orphan rules make the transition graph a hard public contract.
The transition argument list declares the signature required by
transition!. Argument names are documentation
for the contract; only their types participate in the generated
Transition::F signature. For example,
transition Connected => Authenticated(user: String); emits an impl whose
signature is equivalent to:
impl magicstatemachines::Transition<Connected, Authenticated> for ConnectionStandin {
type F = fn(String);
}It does not say what happens to the runtime data. The implementation crate
supplies that effect later with StateMachineImpl!.
use magicstatemachines::{StateMachineDefinition, States};
pub struct ConnectionStandin;
pub mod states {
use magicstatemachines::States;
States! {
Disconnected;
Reconnecting;
Connected;
Authenticated;
Failed;
}
}
use states::*;
StateMachineDefinition! {
for ConnectionStandin;
pub Initial: Disconnected | Reconnecting;
transition Disconnected => Connected | Failed();
transition Connected => Authenticated(user: String);
transition Connected | Authenticated => Disconnected();
transition Authenticated => Connected();
union All: Disconnected | Connected | Authenticated;
union Online: All, Connected | Authenticated;
}| on the left or right of a transition expands to every pair. For
example, transition Authenticated => Connected | Failed(); declares both
Authenticated -> Connected and Authenticated -> Failed with the same
empty signature. transition Connected | Authenticated => Disconnected();
declares two incoming edges into Disconnected. This is only a declaration;
whether the implementation shares a body is decided later by
StateMachineImpl!.
Union declarations are forwarded to StateUnion!.
They can be written here for convenience, but they are still independent of
the stand-in and may also be written separately. A union such as
union Online: Connected | Authenticated; does not add transitions by
itself; it only gives APIs a way to name “any online state”.
pub Initial: is intentionally a public raw-construction capability. If a
state is listed there, any code that can obtain raw implementation data can
call State::new or the generated Runtime::with_state for that state.
Do not list a state as public initial if entering that state must be
controlled by the implementation module. For that case, omit pub Initial:
and use
priv Initial: StateName; inside
StateMachineImpl! to create a private
target-owned construction helper.
A transition declaration must end in ;. Bodies are rejected on purpose:
use magicstatemachines::{StateMachineDefinition, States};
pub struct Standin;
States! { A; B; }
StateMachineDefinition! {
for Standin;
pub Initial: A;
transition A => B() {
// Effects belong in `StateMachineImpl!`, not in the definition.
}
}