macro_rules! StateMachineDefinition {
(
for $standin:ty;
Initial: $first_initial:ident $(| $initial:ident)*;
$($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.
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;
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”.
A transition declaration must end in ;. Bodies are rejected on purpose:
use magicstatemachines::{StateMachineDefinition, States};
pub struct Standin;
States! { A; B; }
StateMachineDefinition! {
for Standin;
Initial: A;
transition A => B() {
// Effects belong in `StateMachineImpl!`, not in the definition.
}
}