macro_rules! create_state_machine {
(
@impl
[$cfg:meta]
[$(#[$($machine_attrs:tt)*])*]
$vis:vis $machine:ident {
states: [ $( $state:ident ),+ $(,)? ],
default: $default:ident,
transitions: [
$(
$action:ident: [ $( $from:ident ),+ $(,)? ] => $to:ident
),+ $(,)?
],
}
) => { ... };
(
@parse
[$($cfgs:meta),*]
[$($machine_attrs:tt)*]
#[cfg($cfg:meta)]
$($rest:tt)*
) => { ... };
(
@parse
[$($cfgs:meta),*]
[$($machine_attrs:tt)*]
#[cfg_attr($($cfg_attr:tt)*)]
$($rest:tt)*
) => { ... };
(
@parse
[$($cfgs:meta),*]
[$($machine_attrs:tt)*]
#[$($machine_attr:tt)*]
$($rest:tt)*
) => { ... };
(
@parse
[$($cfgs:meta),*]
[$($machine_attrs:tt)*]
$vis:vis $machine:ident {
states: [ $( $state:ident ),+ $(,)? ],
default: $default:ident,
transitions: [
$(
$action:ident: [ $( $from:ident ),+ $(,)? ] => $to:ident
),+ $(,)?
],
}
) => { ... };
($($input:tt)*) => { ... };
}Expand description
Build a state enum, transition metadata, runtime transition checks, and typestate proof helpers.
The macro takes a list of states, the default state, and a set of named transitions. Each transition declares one or more source states and a single destination state.
gwr_components::create_state_machine!(
pub JobMachine {
states: [Queued, Running, Complete, Failed],
default: Queued,
transitions: [
start: [Queued] => Running,
finish: [Running] => Complete,
fail: [Queued, Running] => Failed,
retry: [Failed] => Queued,
],
}
);The generated state enum can be used as a runtime state machine. Transition
marker types are named from the machine, action, and Transition suffix.
let mut state = JobMachine::default();
assert_eq!(state, JobMachine::Queued);
assert!(state.apply::<JobMachineStartTransition>());
assert_eq!(state, JobMachine::Running);
assert!(!state.apply::<JobMachineRetryTransition>());
assert_eq!(state, JobMachine::Running);The macro also generates typestate wrappers. Valid transition methods are only implemented for the source states listed in the transition declaration, so invalid transition chains fail to compile.
let queued = JobMachineTypestate::default();
let running = queued.start();
let complete = running.finish();
assert_eq!(complete.into_state(), JobMachineComplete);cfg_attr is rejected because it can expand to a cfg attribute after
macro parsing, which would allow the generated enum to be disabled without
disabling the rest of the generated state-machine items.
gwr_components::create_state_machine!(
#[cfg_attr(all(), cfg(any()))]
pub DisabledByCfgAttr {
states: [Idle, Busy],
default: Idle,
transitions: [
start: [Idle] => Busy,
],
}
);