From 2d3c5214814dbbe5cc93af9bce134c8f2299f25a Mon Sep 17 00:00:00 2001 From: Maxim Deloof <20443795+mdeloof@users.noreply.github.com> Date: Fri, 4 Jul 2025 11:09:24 +0200 Subject: [PATCH] Make context parameter optional in hooks --- README.md | 16 ++-- macro/src/analyze.rs | 3 + macro/src/codegen.rs | 24 ++++-- statig/src/awaitable/into_state_machine.rs | 97 +++++++++++++++++++++- statig/src/blocking/into_state_machine.rs | 97 +++++++++++++++++++++- statig/tests/async_hooks.rs | 3 +- statig/tests/hooks.rs | 3 +- 7 files changed, 222 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 418a294..dd3fbc0 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ The association between states and their handlers is then expressed in the `Stat ```rust impl statig::State for State { - fn call_handler(&mut self, blinky: &mut Blinky, event: &Event) -> Outcome { + fn call_handler(&mut self, blinky: &mut Blinky, event: &Event, context: &mut ()) -> Outcome { match self { State::LedOn { counter } => blinky.led_on(counter, event), State::LedOff { counter } => blinky.led_off(counter, event), @@ -379,7 +379,7 @@ impl statig::State for State { } impl statig::Superstate for Superstate { - fn call_handler(&mut self, blinky: &mut Blinky, event: &Event) -> Outcome { + fn call_handler(&mut self, blinky: &mut Blinky, event: &Event, context: &mut ()) -> Outcome { match self { Superstate::Blinking { counter } => blinky.blinking(counter, event), } @@ -394,7 +394,7 @@ impl statig::State for State { ... - fn call_entry_action(&mut self, blinky: &mut Blinky) { + fn call_entry_action(&mut self, blinky: &mut Blinky, context: &mut ()) { match self { State::LedOn { counter } => blinky.enter_led_on(counter), State::LedOff { counter } => blinky.enter_led_off(counter), @@ -402,7 +402,7 @@ impl statig::State for State { } } - fn call_exit_action(&mut self, blinky: &mut Blinky) { + fn call_exit_action(&mut self, blinky: &mut Blinky, context: &mut ()) { match self { State::LedOn { counter } => blinky.exit_led_on(counter), State::LedOff { counter } => blinky.exit_led_off(counter), @@ -415,13 +415,13 @@ impl statig::Superstate for Superstate { ... - fn call_entry_action(&mut self, blinky: &mut Blinky) { + fn call_entry_action(&mut self, blinky: &mut Blinky, context: &mut ()) { match self { Superstate::Blinking { counter } => blinky.enter_blinking(counter), } } - fn call_exit_action(&mut self, blinky: &mut Blinky) { + fn call_exit_action(&mut self, blinky: &mut Blinky, context: &mut ()) { match self { Superstate::Blinking { counter } => blinky.exit_blinking(counter), } @@ -490,7 +490,9 @@ impl IntoStateMachine for Blinky { type Context<'ctx> = Context; - const INITIAL: fn() -> State = || State::off(10); + fn initial() -> Self::State { + State::off(10) + } } ``` diff --git a/macro/src/analyze.rs b/macro/src/analyze.rs index a5d5128..4cd5e02 100644 --- a/macro/src/analyze.rs +++ b/macro/src/analyze.rs @@ -645,6 +645,9 @@ pub fn analyze_superstate(method: &ImplItemFn, state_machine: &StateMachine) -> Err(_) => abort!(meta, "initial state must be an expression"), } } else if meta.path().is_ident("local_storage") { + if !local_storage.is_empty() { + abort!(meta, "duplicate `local_storage` item") + } match meta.require_list().and_then(|list| { Punctuated::::parse_terminated.parse2(list.tokens.clone()) }) { diff --git a/macro/src/codegen.rs b/macro/src/codegen.rs index b147f17..f6baf21 100644 --- a/macro/src/codegen.rs +++ b/macro/src/codegen.rs @@ -69,7 +69,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { event: &Self::Event<'_>, context: &mut Self::Context<'_>, ) { - #before_dispatch(self, state_or_superstate, event, context); + use statig::blocking::DispatchHook; + #before_dispatch.call_dispatch_hook(self, state_or_superstate, event, context); } ), Mode::Awaitable => quote!( @@ -79,7 +80,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { event: &Self::Event<'_>, context: &mut Self::Context<'_>, ) -> impl core::future::Future { - #before_dispatch(self, state_or_superstate, event, context) + use statig::awaitable::DispatchHook; + #before_dispatch.call_dispatch_hook(self, state_or_superstate, event, context) } ), }, @@ -95,7 +97,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { event: &Self::Event<'_>, context: &mut Self::Context<'_>, ) { - #after_dispatch(self, state_or_superstate, event, context); + use statig::blocking::DispatchHook; + #after_dispatch.call_dispatch_hook(self, state_or_superstate, event, context); } ), Mode::Awaitable => quote!( @@ -105,7 +108,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { event: &Self::Event<'_>, context: &mut Self::Context<'_>, ) -> impl core::future::Future { - #after_dispatch(self, state_or_superstate, event, context) + use statig::awaitable::DispatchHook; + #after_dispatch.call_dispatch_hook(self, state_or_superstate, event, context) } ), }, @@ -121,7 +125,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { target: &Self::State, context: &mut Self::Context<'_>, ) { - #before_transition(self, source, target, context); + use statig::blocking::TransitionHook; + #before_transition.call_transition_hook(self, source, target, context); } ), Mode::Awaitable => quote!( @@ -131,7 +136,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { target: &Self::State, context: &mut Self::Context<'_>, ) -> impl core::future::Future { - #before_transition(self, source, target, context) + use statig::awaitable::TransitionHook; + #before_transition.call_transition_hook(self, source, target, context) } ), }, @@ -147,7 +153,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { target: &Self::State, context: &mut Self::Context<'_>, ) { - #after_transition(self, source, target, context); + use statig::blocking::TransitionHook; + #after_transition.call_transition_hook(self, source, target, context); } ), Mode::Awaitable => quote!( @@ -157,7 +164,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { target: &Self::State, context: &mut Self::Context<'_>, ) -> impl core::future::Future { - #after_transition(self, source, target, context) + use statig::awaitable::TransitionHook; + #after_transition.call_transition_hook(self, source, target, context) } ), }, diff --git a/statig/src/awaitable/into_state_machine.rs b/statig/src/awaitable/into_state_machine.rs index 95c93f6..9859408 100644 --- a/statig/src/awaitable/into_state_machine.rs +++ b/statig/src/awaitable/into_state_machine.rs @@ -2,7 +2,7 @@ use core::future::{self, Future}; use crate::StateOrSuperstate; -/// Trait for transorming a type into a state machine. +/// Trait for transforming a type into a state machine. pub trait IntoStateMachine where Self: Sized, @@ -66,3 +66,98 @@ where future::ready(()) } } + +pub trait DispatchHook +where + T: IntoStateMachine, +{ + fn call_dispatch_hook( + self, + shared_storage: &mut T, + state_or_superstate: StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + event: &T::Event<'_>, + context: &mut T::Context<'_>, + ) -> impl Future; +} + +impl DispatchHook for F +where + T: IntoStateMachine, + F: AsyncFnOnce(&mut T, StateOrSuperstate<'_, T::State, T::Superstate<'_>>, &T::Event<'_>), +{ + fn call_dispatch_hook( + self, + shared_storage: &mut T, + state_or_superstate: StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + event: &T::Event<'_>, + _context: &mut T::Context<'_>, + ) -> impl Future { + (self)(shared_storage, state_or_superstate, event) + } +} + +impl DispatchHook for F +where + T: IntoStateMachine, + F: AsyncFnOnce( + &mut T, + StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + &T::Event<'_>, + &mut T::Context<'_>, + ), +{ + fn call_dispatch_hook( + self, + shared_storage: &mut T, + state_or_superstate: StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + event: &T::Event<'_>, + context: &mut T::Context<'_>, + ) -> impl Future { + (self)(shared_storage, state_or_superstate, event, context) + } +} + +pub trait TransitionHook +where + T: IntoStateMachine, +{ + fn call_transition_hook( + self, + shared_storage: &mut T, + source: &T::State, + target: &T::State, + context: &mut T::Context<'_>, + ) -> impl Future; +} + +impl TransitionHook for F +where + T: IntoStateMachine, + F: AsyncFnOnce(&mut T, &T::State, &T::State), +{ + fn call_transition_hook( + self, + shared_storage: &mut T, + source: &T::State, + target: &T::State, + _context: &mut T::Context<'_>, + ) -> impl Future { + (self)(shared_storage, source, target) + } +} + +impl TransitionHook for F +where + T: IntoStateMachine, + F: AsyncFnOnce(&mut T, &T::State, &T::State, &mut T::Context<'_>), +{ + fn call_transition_hook( + self, + shared_storage: &mut T, + source: &T::State, + target: &T::State, + context: &mut T::Context<'_>, + ) -> impl Future { + (self)(shared_storage, source, target, context) + } +} diff --git a/statig/src/blocking/into_state_machine.rs b/statig/src/blocking/into_state_machine.rs index 2230832..1071746 100644 --- a/statig/src/blocking/into_state_machine.rs +++ b/statig/src/blocking/into_state_machine.rs @@ -1,6 +1,6 @@ use crate::StateOrSuperstate; -/// Trait for transorming a type into a state machine. +/// Trait for transforming a type into a state machine. pub trait IntoStateMachine where Self: Sized, @@ -60,3 +60,98 @@ where ) { } } + +pub trait DispatchHook +where + T: IntoStateMachine, +{ + fn call_dispatch_hook( + &self, + shared_storage: &mut T, + state_or_superstate: StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + event: &T::Event<'_>, + context: &mut T::Context<'_>, + ); +} + +impl DispatchHook,)> for F +where + T: IntoStateMachine, + F: Fn(&mut T, StateOrSuperstate<'_, T::State, T::Superstate<'_>>, &T::Event<'_>), +{ + fn call_dispatch_hook( + &self, + shared_storage: &mut T, + state_or_superstate: StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + event: &T::Event<'_>, + _context: &mut T::Context<'_>, + ) { + (self)(shared_storage, state_or_superstate, event) + } +} + +impl DispatchHook, T::Context<'_>)> for F +where + T: IntoStateMachine, + F: Fn( + &mut T, + StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + &T::Event<'_>, + &mut T::Context<'_>, + ), +{ + fn call_dispatch_hook( + &self, + shared_storage: &mut T, + state_or_superstate: StateOrSuperstate<'_, T::State, T::Superstate<'_>>, + event: &T::Event<'_>, + context: &mut T::Context<'_>, + ) { + (self)(shared_storage, state_or_superstate, event, context) + } +} + +pub trait TransitionHook +where + T: IntoStateMachine, +{ + fn call_transition_hook( + &self, + shared_storage: &mut T, + source: &T::State, + target: &T::State, + context: &mut T::Context<'_>, + ); +} + +impl TransitionHook for F +where + T: IntoStateMachine, + F: Fn(&mut T, &T::State, &T::State), +{ + fn call_transition_hook( + &self, + shared_storage: &mut T, + source: &T::State, + target: &T::State, + _context: &mut T::Context<'_>, + ) { + (self)(shared_storage, source, target) + } +} + +impl TransitionHook,)> for F +where + T: IntoStateMachine, + F: Fn(&mut T, &T::State, &T::State, &mut T::Context<'_>), +{ + fn call_transition_hook( + &self, + shared_storage: &mut T, + source: &T::State, + target: &T::State, + context: &mut T::Context<'_>, + ) { + (self)(shared_storage, source, target, context) + } +} diff --git a/statig/tests/async_hooks.rs b/statig/tests/async_hooks.rs index a9cbdf7..d9c55f6 100644 --- a/statig/tests/async_hooks.rs +++ b/statig/tests/async_hooks.rs @@ -41,7 +41,6 @@ mod tests { &mut self, state_or_superstate: StateOrSuperstate<'_, State, Superstate>, event: &Event, - _context: &mut (), ) { self.before_dispatch = true; } @@ -55,7 +54,7 @@ mod tests { self.after_dispatch = true; } - async fn before_transition(&mut self, source: &State, target: &State, _context: &mut ()) { + async fn before_transition(&mut self, source: &State, target: &State) { self.before_transition = true; } diff --git a/statig/tests/hooks.rs b/statig/tests/hooks.rs index 587fd2f..61d1828 100644 --- a/statig/tests/hooks.rs +++ b/statig/tests/hooks.rs @@ -40,7 +40,6 @@ mod tests { &mut self, state_or_superstate: StateOrSuperstate<'_, State, Superstate>, event: &Event, - _context: &mut (), ) { self.before_dispatch = true; } @@ -54,7 +53,7 @@ mod tests { self.after_dispatch = true; } - fn before_transition(&mut self, source: &State, target: &State, _context: &mut ()) { + fn before_transition(&mut self, source: &State, target: &State) { self.before_transition = true; }