From 39a0771049ca9b0634077d5d8de73f16f2fe4834 Mon Sep 17 00:00:00 2001 From: Martin Broers Date: Wed, 12 Apr 2023 11:05:22 +0200 Subject: [PATCH] Added callback for succesfull transition This patch adds a callback for succesfull transitions. This function can be used to implement a logging service of some sorts for the state machine. Signed-off-by: Martin Broers --- examples/transition_callback.rs | 38 ++++++++++++++++++++++++++++++ macros/src/codegen.rs | 14 ++++++++++- macros/src/parser/mod.rs | 2 ++ macros/src/parser/state_machine.rs | 12 +++++++++- 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 examples/transition_callback.rs diff --git a/examples/transition_callback.rs b/examples/transition_callback.rs new file mode 100644 index 0000000..6722315 --- /dev/null +++ b/examples/transition_callback.rs @@ -0,0 +1,38 @@ +//! An example of using state data to propagate events (See issue-17) + +#![deny(missing_docs)] + +use std::sync::{Arc, Mutex}; + +use smlang::statemachine; + +statemachine! { + generate_transition_callback: true, + transitions: { + *D0 + ToD1 = D1, + D1 + ToD2 = D2, + }, +} + +/// Context +pub struct Context { + transition_called: Arc>, +} + +impl StateMachineContext for Context { + fn transition_callback(&self, _state: &Option) { + *self.transition_called.lock().unwrap() = true; + } +} + +fn main() { + let mut sm = StateMachine::new(Context { + transition_called: Arc::new(Mutex::new(false)), + }); + + // first event starts the dominos + let _ = sm.process_event(Events::ToD1).unwrap(); + + assert!(matches!(sm.state(), Ok(&States::D1))); + assert!(*sm.context().transition_called.lock().unwrap()); +} diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 214d114..f6138a8 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -20,6 +20,7 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { format_ident!("{sm_name}StateMachineContext", span = sm_name_span); let generate_entry_exit_states = sm.generate_entry_exit_states; + let generate_transition_callback = sm.generate_transition_callback; // Get only the unique states let mut state_list: Vec<_> = sm.states.values().collect(); @@ -254,7 +255,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { // get input state lifetimes let in_state_lifetimes = sm.state_data.lifetimes.get(&value.in_state.to_string()).cloned().unwrap_or_default(); - // get output state lifetimes let out_state_lifetimes = sm.state_data.lifetimes.get(&value.out_state.to_string()).cloned().unwrap_or_default(); @@ -494,6 +494,13 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { let derive_states_list = &sm.derive_states; let derive_events_list = &sm.derive_events; + let transition_callback = if generate_transition_callback { + quote!( + self.context().transition_callback(&self.state); + ) + } else { + quote!() + }; // Build the states and events output quote! { /// This trait outlines the guards and actions that need to be implemented for the state @@ -504,6 +511,9 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { #guard_list #action_list #entry_list + + #[allow(missing_docs)] + fn transition_callback(&self, new_state: &Option<#states_type_name>) {} } /// List of auto-generated states. @@ -598,6 +608,8 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { #(#events_type_name::#events => { #code_blocks + #transition_callback + self.state() }),* _ => { diff --git a/macros/src/parser/mod.rs b/macros/src/parser/mod.rs index b6934fd..3460e69 100644 --- a/macros/src/parser/mod.rs +++ b/macros/src/parser/mod.rs @@ -40,6 +40,7 @@ pub struct ParsedStateMachine { pub states_events_mapping: HashMap>, pub generate_entry_exit_states: bool, + pub generate_transition_callback: bool, } // helper function for adding a transition to a transition event map @@ -212,6 +213,7 @@ impl ParsedStateMachine { event_data, states_events_mapping, generate_entry_exit_states: sm.generate_entry_exit_states, + generate_transition_callback: sm.generate_transition_callback, }) } } diff --git a/macros/src/parser/state_machine.rs b/macros/src/parser/state_machine.rs index dfbb187..8dc5674 100644 --- a/macros/src/parser/state_machine.rs +++ b/macros/src/parser/state_machine.rs @@ -10,6 +10,7 @@ pub struct StateMachine { pub derive_states: Vec, pub derive_events: Vec, pub generate_entry_exit_states: bool, + pub generate_transition_callback: bool, } impl StateMachine { @@ -22,6 +23,7 @@ impl StateMachine { derive_states: Vec::new(), derive_events: Vec::new(), generate_entry_exit_states: false, + generate_transition_callback: false, } } @@ -147,6 +149,13 @@ impl parse::Parse for StateMachine { statemachine.generate_entry_exit_states = true } } + "generate_transition_callback" => { + input.parse::()?; + let generate_transition_callback: syn::LitBool = input.parse()?; + if generate_transition_callback.value { + statemachine.generate_transition_callback = true + } + } keyword => { return Err(parse::Error::new( input.span(), @@ -157,7 +166,8 @@ impl parse::Parse for StateMachine { \"custom_guard_error\", \"derive_states\", \"derive_events\", - \"generate_entry_exit_states\" + \"generate_entry_exit_states\", + \"generate_transition_callback\", ]", keyword ),