diff --git a/CHANGELOG.md b/CHANGELOG.md index faf5582..88a2fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ process. - `StateMachine::new` and `StateMachine::new_with_state` are now const functions - Fixed clippy warnings - [breaking] Changed guard functions return type from Result<(),_> to Result +- [breaking] Changed action functions return type from () to Result ## [v0.6.0] - 2022-11-02 diff --git a/examples/async.rs b/examples/async.rs index 7e7e357..e13bbe0 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -39,22 +39,24 @@ impl StateMachineContext for Context { Ok(true) } - async fn action2(&mut self) -> () { + async fn action2(&mut self) -> Result<(), ()> { println!("`action2` called from async context"); if !*self.lock.read().await { self.done = true; } + Ok(()) } - async fn action1(&mut self) -> () { + async fn action1(&mut self) -> Result<(), ()> { println!("`action1` called from async context"); let mut lock = self.lock.write().await; *lock = true; + Ok(()) } - fn action3(&mut self) -> bool { + fn action3(&mut self) -> Result { println!("`action3` called from sync context, done = `{}`", self.done); - self.done + Ok(self.done) } } diff --git a/examples/context.rs b/examples/context.rs index 171abe5..f28b8a4 100644 --- a/examples/context.rs +++ b/examples/context.rs @@ -20,12 +20,14 @@ pub struct Context { } impl StateMachineContext for Context { - fn count_transition1(&mut self) { + fn count_transition1(&mut self) -> Result<(), ()> { self.num_transitions += 1; + Ok(()) } - fn count_transition2(&mut self) { + fn count_transition2(&mut self) -> Result<(), ()> { self.num_transitions += 1; + Ok(()) } } diff --git a/examples/dominos.rs b/examples/dominos.rs index e25b138..37e5d3c 100644 --- a/examples/dominos.rs +++ b/examples/dominos.rs @@ -18,20 +18,20 @@ statemachine! { pub struct Context; impl StateMachineContext for Context { - fn to_d2(&mut self) -> Option { - Some(Events::ToD2) + fn to_d2(&mut self) -> Result, ()> { + Ok(Some(Events::ToD2)) } - fn to_d3(&mut self, _state_data: &Option) -> Option { - Some(Events::ToD3) + fn to_d3(&mut self, _state_data: &Option) -> Result, ()> { + Ok(Some(Events::ToD3)) } - fn to_d4(&mut self, _state_data: &Option) -> Option { - Some(Events::ToD4) + fn to_d4(&mut self, _state_data: &Option) -> Result, ()> { + Ok(Some(Events::ToD4)) } - fn to_d5(&mut self, _state_data: &Option) -> Option { - Some(Events::ToD5) + fn to_d5(&mut self, _state_data: &Option) -> Result, ()> { + Ok(Some(Events::ToD5)) } } diff --git a/examples/event_with_data.rs b/examples/event_with_data.rs index c222973..ea50a92 100644 --- a/examples/event_with_data.rs +++ b/examples/event_with_data.rs @@ -25,8 +25,9 @@ impl StateMachineContext for Context { Ok(event_data == &MyEventData(42)) } - fn action(&mut self, event_data: MyEventData) { + fn action(&mut self, event_data: MyEventData) -> Result<(), ()> { println!("Got valid Event Data = {}", event_data.0); + Ok(()) } } diff --git a/examples/event_with_mutable_data.rs b/examples/event_with_mutable_data.rs index 560bb99..c2c8368 100644 --- a/examples/event_with_mutable_data.rs +++ b/examples/event_with_mutable_data.rs @@ -26,8 +26,9 @@ impl StateMachineContext for Context { Ok(true) } - fn action(&mut self, event_data: &mut MyEventData) { + fn action(&mut self, event_data: &mut MyEventData) -> Result<(), ()> { println!("Got valid Event Data = {}", event_data.0); + Ok(()) } } diff --git a/examples/event_with_reference_data.rs b/examples/event_with_reference_data.rs index 4c6afe9..8e69480 100644 --- a/examples/event_with_reference_data.rs +++ b/examples/event_with_reference_data.rs @@ -26,16 +26,18 @@ impl StateMachineContext for Context { Ok(!event_data.is_empty()) } - fn action1(&mut self, event_data: &[u8]) { + fn action1(&mut self, event_data: &[u8]) -> Result<(), ()> { println!("Got valid Event Data = {:?}", event_data); + Ok(()) } fn guard2(&mut self, event_data: &MyReferenceWrapper) -> Result { Ok(*event_data.0 > 9000) } - fn action2(&mut self, event_data: MyReferenceWrapper) { + fn action2(&mut self, event_data: MyReferenceWrapper) -> Result<(), ()> { println!("Got valid Event Data = {}", event_data.0); + Ok(()) } } diff --git a/examples/ex3.rs b/examples/ex3.rs index 0879ff3..10c3a36 100644 --- a/examples/ex3.rs +++ b/examples/ex3.rs @@ -28,12 +28,14 @@ impl StateMachineContext for Context { Ok(false) } - fn action1(&mut self) { + fn action1(&mut self) -> Result<(), ()> { //println!("Action 1"); + Ok(()) } - fn action2(&mut self) { + fn action2(&mut self) -> Result<(), ()> { //println!("Action 1"); + Ok(()) } } diff --git a/examples/guard_action_syntax.rs b/examples/guard_action_syntax.rs index f3b953f..77d43e0 100644 --- a/examples/guard_action_syntax.rs +++ b/examples/guard_action_syntax.rs @@ -32,7 +32,7 @@ impl StateMachineContext for Context { } // Action1 has access to the data from Event1, and need to return the state data for State2 - fn action1(&mut self, _event_data: MyEventData) -> MyStateData { + fn action1(&mut self, _event_data: MyEventData) -> Result { todo!() } @@ -42,7 +42,7 @@ impl StateMachineContext for Context { } // Action2 has access to the data from State2 - fn action2(&mut self, _state_data: &MyStateData) { + fn action2(&mut self, _state_data: &MyStateData) -> Result<(), ()> { todo!() } } diff --git a/examples/guard_action_syntax_with_temporary_context.rs b/examples/guard_action_syntax_with_temporary_context.rs index 69b4be2..a062575 100644 --- a/examples/guard_action_syntax_with_temporary_context.rs +++ b/examples/guard_action_syntax_with_temporary_context.rs @@ -35,10 +35,14 @@ impl StateMachineContext for Context { } // Action1 has access to the data from Event1, and need to return the state data for State2 - fn action1(&mut self, temp_context: &mut u16, _event_data: MyEventData) -> MyStateData { + fn action1( + &mut self, + temp_context: &mut u16, + _event_data: MyEventData, + ) -> Result { *temp_context += 1; - MyStateData(1) + Ok(MyStateData(1)) } // Guard2 has access to the data from State2 @@ -49,8 +53,9 @@ impl StateMachineContext for Context { } // Action2 has access to the data from State2 - fn action2(&mut self, temp_context: &mut u16, _state_data: &MyStateData) { + fn action2(&mut self, temp_context: &mut u16, _state_data: &MyStateData) -> Result<(), ()> { *temp_context += 1; + Ok(()) } } diff --git a/examples/guard_custom_error.rs b/examples/guard_custom_error.rs index e7437ca..e6f9839 100644 --- a/examples/guard_custom_error.rs +++ b/examples/guard_custom_error.rs @@ -40,7 +40,7 @@ impl StateMachineContext for Context { } // Action1 has access to the data from Event1, and need to return the state data for State2 - fn action1(&mut self, _event_data: MyEventData) -> MyStateData { + fn action1(&mut self, _event_data: MyEventData) -> Result { todo!() } @@ -50,7 +50,7 @@ impl StateMachineContext for Context { } // Action2 has access to the data from State2 - fn action2(&mut self, _state_data: &MyStateData) { + fn action2(&mut self, _state_data: &MyStateData) -> Result<(), Self::GuardError> { todo!() } } diff --git a/examples/named_async.rs b/examples/named_async.rs index e34f201..ac03063 100644 --- a/examples/named_async.rs +++ b/examples/named_async.rs @@ -35,22 +35,24 @@ impl AsyncSimpleStateMachineContext for Context { Ok(true) } - async fn action1(&mut self) -> () { + fn action3(&mut self) -> Result { + println!("`action3` called from sync context, done = `{}`", self.done); + Ok(self.done) + } + + async fn action1(&mut self) -> Result<(), ()> { println!("`action1` called from async context"); let mut lock = self.lock.write().await; *lock = true; + Ok(()) } - fn action3(&mut self) -> bool { - println!("`action3` called from sync context, done = `{}`", self.done); - self.done - } - - async fn action2(&mut self) -> () { + async fn action2(&mut self) -> Result<(), ()> { println!("`action2` called from async context"); if !*self.lock.read().await { self.done = true; } + Ok(()) } } diff --git a/examples/named_dominos.rs b/examples/named_dominos.rs index 8b258d1..a250e5b 100644 --- a/examples/named_dominos.rs +++ b/examples/named_dominos.rs @@ -19,20 +19,20 @@ statemachine! { pub struct Context; impl DominosStateMachineContext for Context { - fn to_d2(&mut self) -> Option { - Some(DominosEvents::ToD2) + fn to_d2(&mut self) -> Result, ()> { + Ok(Some(DominosEvents::ToD2)) } - fn to_d3(&mut self, _state_data: &Option) -> Option { - Some(DominosEvents::ToD3) + fn to_d3(&mut self, _state_data: &Option) -> Result, ()> { + Ok(Some(DominosEvents::ToD3)) } - fn to_d4(&mut self, _state_data: &Option) -> Option { - Some(DominosEvents::ToD4) + fn to_d4(&mut self, _state_data: &Option) -> Result, ()> { + Ok(Some(DominosEvents::ToD4)) } - fn to_d5(&mut self, _state_data: &Option) -> Option { - Some(DominosEvents::ToD5) + fn to_d5(&mut self, _state_data: &Option) -> Result, ()> { + Ok(Some(DominosEvents::ToD5)) } } diff --git a/examples/named_ex3.rs b/examples/named_ex3.rs index 7ae0ce7..062ce30 100644 --- a/examples/named_ex3.rs +++ b/examples/named_ex3.rs @@ -29,12 +29,14 @@ impl LoopingWithGuardsStateMachineContext for Context { Ok(false) } - fn action1(&mut self) { + fn action1(&mut self) -> Result<(), ()> { //println!("Action 1"); + Ok(()) } - fn action2(&mut self) { + fn action2(&mut self) -> Result<(), ()> { //println!("Action 1"); + Ok(()) } } diff --git a/examples/named_state_with_data.rs b/examples/named_state_with_data.rs index 9d73237..08edfa9 100644 --- a/examples/named_state_with_data.rs +++ b/examples/named_state_with_data.rs @@ -23,8 +23,8 @@ statemachine! { pub struct Context; impl StatesWithDataStateMachineContext for Context { - fn action(&mut self) -> MyStateData { - MyStateData(42) + fn action(&mut self) -> Result { + Ok(MyStateData(42)) } } diff --git a/examples/named_state_with_reference_data.rs b/examples/named_state_with_reference_data.rs index aef713d..5dbf199 100644 --- a/examples/named_state_with_reference_data.rs +++ b/examples/named_state_with_reference_data.rs @@ -23,8 +23,8 @@ statemachine! { pub struct Context; impl StatesWithRefDataStateMachineContext for Context { - fn action<'a>(&mut self) -> MyStateData<'a> { - MyStateData(&42) + fn action<'a>(&mut self) -> Result, ()> { + Ok(MyStateData(&42)) } } diff --git a/examples/reuse_action.rs b/examples/reuse_action.rs index 46c401a..4f02d88 100644 --- a/examples/reuse_action.rs +++ b/examples/reuse_action.rs @@ -18,8 +18,9 @@ statemachine! { pub struct Context(usize); impl StateMachineContext for Context { - fn action(&mut self) { + fn action(&mut self) -> Result<(), ()> { self.0 += 1; + Ok(()) } } diff --git a/examples/starting_state_with_data.rs b/examples/starting_state_with_data.rs index 720ce45..82537f6 100644 --- a/examples/starting_state_with_data.rs +++ b/examples/starting_state_with_data.rs @@ -22,8 +22,8 @@ statemachine! { pub struct Context; impl StateMachineContext for Context { - fn action(&mut self) -> MyStateData { - MyStateData(42) + fn action(&mut self) -> Result { + Ok(MyStateData(42)) } } diff --git a/examples/state_machine_logger.rs b/examples/state_machine_logger.rs index 3374627..09a5d7c 100644 --- a/examples/state_machine_logger.rs +++ b/examples/state_machine_logger.rs @@ -35,9 +35,9 @@ impl StateMachineContext for Context { } // Action1 has access to the data from Event1, and need to return the state data for State2 - fn action1(&mut self, event_data: MyEventData) -> MyStateData { + fn action1(&mut self, event_data: MyEventData) -> Result { println!("Creating state data for next state"); - MyStateData(event_data.0) + Ok(MyStateData(event_data.0)) } // Guard2 has access to the data from State2 @@ -46,8 +46,9 @@ impl StateMachineContext for Context { } // Action2 has access to the data from State2 - fn action2(&mut self, state_data: &MyStateData) { + fn action2(&mut self, state_data: &MyStateData) -> Result<(), ()> { println!("Printing state data {:?}", state_data); + Ok(()) } fn log_process_event(&self, current_state: &States, event: &Events) { diff --git a/examples/state_with_data.rs b/examples/state_with_data.rs index fb5fb84..bb59a28 100644 --- a/examples/state_with_data.rs +++ b/examples/state_with_data.rs @@ -22,8 +22,8 @@ statemachine! { pub struct Context; impl StateMachineContext for Context { - fn action(&mut self) -> MyStateData { - MyStateData(42) + fn action(&mut self) -> Result { + Ok(MyStateData(42)) } } diff --git a/examples/state_with_reference_data.rs b/examples/state_with_reference_data.rs index 134b254..8a88a65 100644 --- a/examples/state_with_reference_data.rs +++ b/examples/state_with_reference_data.rs @@ -22,8 +22,8 @@ statemachine! { pub struct Context; impl StateMachineContext for Context { - fn action<'a>(&mut self) -> MyStateData<'a> { - MyStateData(&42) + fn action<'a>(&mut self) -> Result, ()> { + Ok(MyStateData(&42)) } } diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 2b02d96..affd452 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -2,9 +2,9 @@ use crate::parser::transition::visit_guards; use crate::parser::{lifetimes::Lifetimes, AsyncIdent, ParsedStateMachine}; -use proc_macro2::{Span, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; -use syn::{punctuated::Punctuated, token::Paren, Type, TypeTuple}; +use syn::Type; pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { let (sm_name, sm_name_span) = sm @@ -203,6 +203,12 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { }) .collect(); + let guard_error = if sm.custom_guard_error { + quote! { Self::GuardError } + } else { + quote! { () } + }; + let out_states: Vec>> = transitions .values() .map(|event_mappings| { @@ -314,12 +320,6 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { None => quote! {}, }; - let guard_error = if sm.custom_guard_error { - quote! { Self::GuardError } - } else { - quote! { () } - }; - // Only add the guard if it hasn't been added before if !guard_set.iter().any(|g| g == guard) { guard_set.push(guard.clone()); @@ -350,15 +350,10 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { .data_types .get(&transition.out_state.to_string()) { - output_data.clone() + quote! { Result<#output_data,#guard_error> } } else { // Empty return type - Type::Tuple(TypeTuple { - paren_token: Paren { - span: Span::call_site(), - }, - elems: Punctuated::new(), - }) + quote! { Result<(),#guard_error> } }; let event_data = match sm.event_data.data_types.get(event) { @@ -430,7 +425,7 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { self.context.#exit_ident(); self.context.#entry_ident(); }; - let (is_async_action, action_code) = generate_action(action, &temporary_context_call, action_params); + let (is_async_action, action_code) = generate_action(action, &temporary_context_call, action_params, &error_type_name); is_async_state_machine |= is_async_action; if let Some(expr) = guard { // Guarded transition @@ -468,7 +463,8 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { } } } else { // Unguarded transition - quote!{ + + quote!{ #action_code let out_state = #states_type_name::#out_state; self.context.log_state_change(&out_state); @@ -611,6 +607,8 @@ pub fn generate_code(sm: &ParsedStateMachine) -> proc_macro2::TokenStream { TransitionsFailed, /// When guard is failed. GuardFailed(T), + /// When action returns Err + ActionFailed(T), } /// State machine structure definition. @@ -686,6 +684,7 @@ fn generate_action( action: &Option, temporary_context_call: &TokenStream, g_a_param: &TokenStream, + error_type_name: &Ident, ) -> (bool, TokenStream) { let mut is_async = false; let code = if let Some(AsyncIdent { @@ -701,7 +700,7 @@ fn generate_action( }; quote! { // ACTION - let _data = self.context.#action_ident(#temporary_context_call #g_a_param) #action_await; + let _data = self.context.#action_ident(#temporary_context_call #g_a_param) #action_await .map_err(#error_type_name::ActionFailed)?; self.context.log_action(stringify!(#action_ident)); } } else { diff --git a/tests/test.rs b/tests/test.rs index 7fe80b3..65a1c48 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -60,15 +60,21 @@ fn multiple_lifetimes() { Ok(true) } - fn action1<'a>(&mut self, event_data: &'a X) -> &'a X { - event_data + fn action1<'a>(&mut self, event_data: &'a X) -> Result<&'a X, ()> { + Ok(event_data) } - fn action2<'a, 'b>(&mut self, state_data: &'a X, event_data: &'b Y) -> (&'a X, &'b Y) { - (state_data, event_data) + fn action2<'a, 'b>( + &mut self, + state_data: &'a X, + event_data: &'b Y, + ) -> Result<(&'a X, &'b Y), ()> { + Ok((state_data, event_data)) } - fn action3(&mut self, _event_data: &Z) {} + fn action3(&mut self, _event_data: &Z) -> Result<(), ()> { + Ok(()) + } } #[allow(dead_code)] @@ -144,8 +150,8 @@ fn async_guards_and_actions() { Ok(true) } - async fn action1(&mut self) -> () { - () + async fn action1(&mut self) -> Result<(), ()> { + Ok(()) } } @@ -186,11 +192,13 @@ fn guard_expressions() { fn too_many_attempts(&mut self, _e: &Entry) -> Result { Ok(self.attempts >= 3) } - fn reset(&mut self) { + fn reset(&mut self) -> Result<(), ()> { self.attempts = 0; + Ok(()) } - fn attempt(&mut self, _e: &Entry) { + fn attempt(&mut self, _e: &Entry) -> Result<(), ()> { self.attempts += 1; + Ok(()) } } @@ -256,8 +264,9 @@ fn guarded_transition_before_unguarded() { Ok(self.enabled) } - fn disable(&mut self) { + fn disable(&mut self) -> Result<(), ()> { self.enabled = false; + Ok(()) } } let mut sm = StateMachine::new(Context { enabled: true });