Skip to main content

create_state_machine

Macro create_state_machine 

Source
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,
        ],
    }
);