diff --git a/README.md b/README.md index ae7fbb0..7ac9dc4 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,11 @@ function. An example is available in `on_entry_on_exit`. +There is also a generic flag available, `generate_on_entry_on_exit`, which will +generate for all states in the statemachine an entry and an exit function. If +they are not used, they will be optimized away by the compiler. An example be +found in `on_entry_on_exit_generic`. + ## Helpers ### Auto-derive certain traits for states and events diff --git a/examples/on_entry_on_exit_generic.rs b/examples/on_entry_on_exit_generic.rs new file mode 100644 index 0000000..bde15e5 --- /dev/null +++ b/examples/on_entry_on_exit_generic.rs @@ -0,0 +1,70 @@ +//! An example of using state data to propagate events (See issue-17) + +#![deny(missing_docs)] + +use smlang::statemachine; + +statemachine! { + name: OnEntryExample, + generate_entry_exit_states: true, + transitions: { + *D0 < exit_d0 + ToD1 = D1, + D0 + ToD3 = D3, + D1 + ToD2 = D2, + D2 + ToD1 = D1, + D1 + ToD0 = D0, + }, +} + +/// Context +pub struct Context { + exited_d0: i32, + entered_d1: i32, +} + +impl OnEntryExampleStateMachineContext for Context { + fn on_exit_d0(&mut self) { + self.exited_d0 += 1; + } + fn on_entry_d1(&mut self) { + self.entered_d1 += 1; + } +} + +fn main() { + let mut sm = OnEntryExampleStateMachine::new(Context { + exited_d0: 0, + entered_d1: 0, + }); + + // first event starts the dominos + let _ = sm.process_event(OnEntryExampleEvents::ToD1).unwrap(); + + assert!(matches!(sm.state(), Ok(&OnEntryExampleStates::D1))); + assert_eq!(sm.context().exited_d0, 1); + assert_eq!(sm.context().entered_d1, 1); + + let _ = sm.process_event(OnEntryExampleEvents::ToD2).unwrap(); + + assert!(matches!(sm.state(), Ok(&OnEntryExampleStates::D2))); + assert_eq!(sm.context().exited_d0, 1); + assert_eq!(sm.context().entered_d1, 1); + + let _ = sm.process_event(OnEntryExampleEvents::ToD1).unwrap(); + + assert!(matches!(sm.state(), Ok(&OnEntryExampleStates::D1))); + assert_eq!(sm.context().exited_d0, 1); + assert_eq!(sm.context().entered_d1, 2); + + let _ = sm.process_event(OnEntryExampleEvents::ToD0).unwrap(); + + assert!(matches!(sm.state(), Ok(&OnEntryExampleStates::D0))); + assert_eq!(sm.context().exited_d0, 1); + assert_eq!(sm.context().entered_d1, 2); + + let _ = sm.process_event(OnEntryExampleEvents::ToD3).unwrap(); + + assert!(matches!(sm.state(), Ok(&OnEntryExampleStates::D3))); + assert_eq!(sm.context().exited_d0, 2); + assert_eq!(sm.context().entered_d1, 2); +} diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 416406e..0aee15b 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -21,8 +21,8 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { let generate_entry_exit_states = sm.generate_entry_exit_states; let generate_transition_callback = sm.generate_transition_callback; - let entry_fns = &sm.entry_functions; - let exit_fns = &sm.exit_functions; + let mut entry_fns = sm.entry_functions.clone(); + let mut exit_fns = sm.exit_functions.clone(); // Get only the unique states let mut state_list: Vec<_> = sm.states.values().collect(); @@ -226,8 +226,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { let mut guard_list = proc_macro2::TokenStream::new(); let mut action_list = proc_macro2::TokenStream::new(); - let mut entry_list = proc_macro2::TokenStream::new(); - let mut entries_exits = proc_macro2::TokenStream::new(); for ident in entry_fns.values() { entries_exits.extend(quote! { @@ -252,15 +250,17 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { if generate_entry_exit_states { let entry_ident = format_ident!("on_entry_{}", string_morph::to_snake_case(state)); - entry_list.extend(quote! { + entries_exits.extend(quote! { #[allow(missing_docs)] fn #entry_ident(&mut self){} }); + entry_fns.insert(format_ident!("{}", state), entry_ident); let exit_ident = format_ident!("on_exit_{}", string_morph::to_snake_case(state)); - entry_list.extend(quote! { + entries_exits.extend(quote! { #[allow(missing_docs)] fn #exit_ident(&mut self){} }); + exit_fns.insert(format_ident!("{}", state), exit_ident); }; value.iter().for_each(|(event, value)| { @@ -402,7 +402,7 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { let exit_ident = exit_fns.iter().find(|(key, _)| *key == in_state_string).map(|(_, value)|value); let mut has_exit = false; - let exit_ident= if let Some(exit_ident) = exit_ident { + let exit_ident = if let Some(exit_ident) = exit_ident { has_exit = true; quote! { #exit_ident} } else { @@ -410,7 +410,7 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { }; let entry_exit_states = if has_entry && has_exit { - quote! { + quote! { self.context_mut().#exit_ident(); self.context_mut().#entry_ident(); } @@ -568,7 +568,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { #guard_error #guard_list #action_list - #entry_list #entries_exits diff --git a/macros/src/parser/mod.rs b/macros/src/parser/mod.rs index c119654..fc80d25 100644 --- a/macros/src/parser/mod.rs +++ b/macros/src/parser/mod.rs @@ -143,10 +143,6 @@ impl ParsedStateMachine { map.insert(input_state.ident.clone(), identifier.ident.clone()) { if identifier.ident != existing_identifier { - println!( - "entry_state: {:?}, state.ident: {:?}", - identifier.ident, input_state.ident - ); return Err(parse::Error::new( Span::call_site(), "Different entry or exit functions defined for state",