finite_state_machine/
lib.rs1#![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}