Skip to content

Commit

Permalink
Added transition_callback
Browse files Browse the repository at this point in the history
This patch adds a transition_callback function. The function, if not
used, will be optimized away by the compiler.

Signed-off-by: Martin Broers <[email protected]>
  • Loading branch information
Martin Broers committed Jul 5, 2024
1 parent 32d7513 commit 43694af
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 3 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,12 @@ The statemachine will create for all states an `on_entry_` and `on_exit_` functi
If the are not used, they will be optimized away by the compiler. An example be
found in `on_entry_on_exit_generic`.

### Transition callback

The statemachine will call for every transition a transition callback. This function
is called with both the old state and new state as arguments. An example can be found
in `transition_callback`.

## Helpers

### Auto-derive certain traits for states and events
Expand Down
50 changes: 50 additions & 0 deletions examples/transition_callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! An example of using state data to propagate events (See issue-17)
#![deny(missing_docs)]

use std::rc::Rc;
use std::sync::Mutex;

use smlang::statemachine;

statemachine! {
derive_states: [Debug, Copy, Clone ],
transitions: {
*D0 + ToD1 = D1,
D1 + ToD2 = D2,
},
}

/// Context
pub struct Context {
transition_called: Rc<Mutex<bool>>,
state_exited: Rc<Mutex<Option<States>>>,
state_entered: Rc<Mutex<Option<States>>>,
}

impl StateMachineContext for Context {
fn transition_callback(&self, exit: &States, entry: &States) {
*self.transition_called.lock().unwrap() = true;
*self.state_exited.lock().unwrap() = Some(*exit);
*self.state_entered.lock().unwrap() = Some(*entry);
}
}

fn main() {
let mut sm = StateMachine::new(Context {
transition_called: Rc::new(Mutex::new(false)),
state_exited: Rc::new(Mutex::new(None)),
state_entered: Rc::new(Mutex::new(None)),
});

// first event starts the dominos
let _ = sm.process_event(Events::ToD1).unwrap();

assert!(matches!(sm.state(), &States::D1));
assert!(*sm.context().transition_called.lock().unwrap());
assert_eq!(*sm.context().state_exited.lock().unwrap(), Some(States::D0));
assert_eq!(
*sm.context().state_entered.lock().unwrap(),
Some(States::D1)
);
}
10 changes: 7 additions & 3 deletions macros/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
let streams: Vec<TokenStream> =
guard.iter()
.zip(action.iter().zip(out_state)).map(|(guard, (action,out_state))| {

let binding = out_state.to_string();
let out_state_string = &binding.split('(').next().unwrap();
let binding = in_state.to_string();
Expand Down Expand Up @@ -441,7 +440,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
self.context.#guard_ident(#temporary_context_call #guard_params) #guard_await .map_err(#error_type_name::GuardFailed)?
}
});

quote! {
// This #guard_expression contains a boolean expression of guard functions
// Each guard function has Result<bool,_> return type.
Expand All @@ -458,17 +456,18 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
let out_state = #states_type_name::#out_state;
self.context.log_state_change(&out_state);
#entry_exit_states
self.context().transition_callback(&self.state(), &out_state);
self.state = out_state;
return Ok(&self.state);
}
}
} else { // Unguarded transition

quote!{
#action_code
let out_state = #states_type_name::#out_state;
self.context.log_state_change(&out_state);
#entry_exit_states
self.context().transition_callback(&self.state(), &out_state);
self.state = out_state;
return Ok(&self.state);
}
Expand Down Expand Up @@ -569,6 +568,11 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream {
/// `process_event()`. No-op by default but can be overridden in implementations
/// of a state machine's `StateMachineContext` trait.
fn log_state_change(&self, new_state: & #states_type_name) {}

/// Called when transitioning to a new state as a result of an event passed to
/// `process_event()`. No-op by default but can be overridden in implementations
/// of a state machine's `StateMachineContext` trait.
fn transition_callback(&self, old_state: & #states_type_name, new_state: & #states_type_name) {}
}

/// List of auto-generated states.
Expand Down

0 comments on commit 43694af

Please sign in to comment.