finite_state_machine/
lib.rs

1#![no_std]
2pub use paste::paste;
3#[macro_export]
4macro_rules! state_machine {
5    (
6        $name:ident($data:ident$(<$($lt:lifetime),*>)?);
7        $(
8            $state_name:ident {
9                $($event:ident => $possible_target_state:ident),*
10            }
11        ),*
12    ) => {
13        $crate::paste!{
14        mod [<$name:snake>] {
15            use super::*;
16            #[derive(Debug, Default)]
17            pub enum State {
18                #[default]
19            $(
20                $state_name,
21            )*
22                Invalid(&'static str),
23                End
24            }
25            #[cfg_attr(feature = "verbose", derive(Debug))]
26            #[cfg_attr(feature = "derive_default", derive(Default))]
27            pub struct $name$(<$($lt),*>)? {
28                pub state: State,
29                pub data: $data$(<$($lt),*>)?,
30            }
31            enum Events {
32            $(
33                $([<$state_name $event>],)*
34            )*
35            }
36
37            $(
38                pub enum [<$state_name Events>] {
39                    $($event,)*
40                    Illegal(&'static str)
41                }
42            )*
43            pub trait Deciders {
44            $(
45                fn [<$state_name:snake>](&self) -> [<$state_name Events>];
46            )*
47            }
48            $(
49                pub trait [<$state_name Transitions>] {
50                    $(fn [<$event:snake>](&mut self) -> Result<(),&'static str>;)*
51                    fn illegal(&mut self);
52                }
53            )*
54            impl$(<$($lt),*>)? $name$(<$($lt),*>)? {
55                pub fn run_to_end(&mut self) -> Result<(), &'static str> {
56                    loop {
57                        #[cfg(feature = "verbose")]
58                        println!("Debug: {:?}", self.data);
59                        match &self.state {
60                            $(State::$state_name => match self.[<$state_name:snake>]() {
61                                $([<$state_name Events>]::$event => {
62                                    match [<$state_name Transitions>]::[<$event:snake>](self) {
63                                        Ok(_) => {
64                                            #[cfg(feature = "verbose")]
65                                            println!("{} + {} -> {}", stringify!($state_name), stringify!($event), stringify!($possible_target_state));
66                                            self.state = State::$possible_target_state
67                                        },
68                                        Err(message) => {
69                                            #[cfg(feature = "verbose")]
70                                            println!("{} + {} + error({}) -> {}", stringify!($state_name), stringify!($event), message, stringify!(Invalid));
71                                            self.state = State::Invalid(message)
72                                        }
73                                    }
74
75                                },)*
76                                [<$state_name Events>]::Illegal(message) => {
77                                    [<$state_name Transitions>]::illegal(self);
78                                    #[cfg(feature = "verbose")]
79                                    println!("{} + illegal -> invalid({})", stringify!($state_name), stringify!(message));
80                                    self.state = State::Invalid(message);
81                                }
82                            } ,)*
83                            State::End => return Ok(()),
84                            State::Invalid(message) => return Err(message),
85                        };
86                    };
87                }
88            }
89        }
90        }
91    };
92}