From e8a39f4fcff7ff790730cad77dd4777f91f3082f Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Mon, 21 Oct 2024 19:27:53 +0200 Subject: [PATCH 01/13] dirty hack to make async transition work --- Cargo.toml | 1 + examples/no_macro/blinky_async/Cargo.toml | 10 ++ examples/no_macro/blinky_async/src/main.rs | 124 +++++++++++++++++++++ statig/src/awaitable/state_machine.rs | 112 ++++++++++++++----- statig/src/inner.rs | 4 +- 5 files changed, 223 insertions(+), 28 deletions(-) create mode 100644 examples/no_macro/blinky_async/Cargo.toml create mode 100644 examples/no_macro/blinky_async/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 9eff9a3..6748de7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "examples/no_macro/basic", "examples/no_macro/blinky", + "examples/no_macro/blinky_async", "examples/no_macro/bench", "examples/no_macro/history", "examples/no_macro/calculator", diff --git a/examples/no_macro/blinky_async/Cargo.toml b/examples/no_macro/blinky_async/Cargo.toml new file mode 100644 index 0000000..c656cce --- /dev/null +++ b/examples/no_macro/blinky_async/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "no_macro_blink_async" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +statig = { path = "../../../statig", features = ["async"] } +tokio = { version = "*", features = ["full"] } diff --git a/examples/no_macro/blinky_async/src/main.rs b/examples/no_macro/blinky_async/src/main.rs new file mode 100644 index 0000000..a574922 --- /dev/null +++ b/examples/no_macro/blinky_async/src/main.rs @@ -0,0 +1,124 @@ +#![allow(unused)] + +use statig::awaitable::{self, *}; +use std::{future::Future, io::Write, pin::Pin}; + +#[derive(Default)] +pub struct Blinky; + +// The event that will be handled by the state machine. +pub enum Event { + TimerElapsed, + ButtonPressed, +} + +// The enum representing all states of the state machine. These are +// the states you can actually transition to. +#[derive(Debug)] +pub enum State { + LedOn, + LedOff, + NotBlinking, +} + +// The enum representing the superstates of the system. You can not transition +// to a superstate, but instead they define shared behavior of underlying states or +// superstates. +pub enum Superstate { + Blinking, +} + +// The `statig` trait needs to be implemented on the type that will +// imlement the state machine. +impl IntoStateMachine for Blinky { + /// The enum that represents the state. + type State = State; + + type Superstate<'sub> = Superstate; + + /// The event type that will be submitted to the state machine. + type Event<'evt> = Event; + + type Context<'ctx> = (); + + /// The initial state of the state machine. + const INITIAL: State = State::LedOn; + +} + +impl IntoStateMachineExt for Blinky { + + async fn on_transition(&mut self, from_state: &State, to_state: &State) { + println!("Transitioned to {:?}", to_state); + } + +} + +// Implement the `statig::State` trait for the state enum. +impl awaitable::State for State { + fn call_handler<'fut>(&'fut mut self, blinky: &'fut mut Blinky, event: &'fut Event, _: &'fut mut ()) -> Pin> + Send + 'fut)>>{ + match self { + State::LedOn => Box::pin(Blinky::led_on(event)), + State::LedOff => Box::pin(Blinky::led_off(event)), + State::NotBlinking => Box::pin(Blinky::not_blinking(event)), + } + } + + fn superstate<'fut>(&mut self) -> Option { + match self { + State::LedOn => Some(Superstate::Blinking), + State::LedOff => Some(Superstate::Blinking), + State::NotBlinking => None, + } + } +} + +// Implement the `statig::Superstate` trait for the superstate enum. +impl awaitable::Superstate for Superstate { + fn call_handler<'fut>(&'fut mut self, blinky: &'fut mut Blinky, event: &'fut Event, _: &'fut mut ()) -> Pin> + Send + 'fut)>> { + Box::pin(match self { + Superstate::Blinking => Blinky::blinking(event), + }) + } +} + +impl Blinky { + async fn led_on(event: &Event) -> Response { + match event { + Event::TimerElapsed => Transition(State::LedOff), + _ => Super, + } + } + + async fn led_off(event: &Event) -> Response { + match event { + Event::TimerElapsed => Transition(State::LedOn), + _ => Super, + } + } + + async fn blinking(event: &Event) -> Response { + match event { + Event::ButtonPressed => Transition(State::NotBlinking), + _ => Super, + } + } + + async fn not_blinking(event: &Event) -> Response { + match event { + Event::ButtonPressed => Transition(State::LedOn), + _ => Super, + } + } + +} + +#[tokio::main] +async fn main() { + let mut state_machine = Blinky::default().state_machine(); + + state_machine.handle(&Event::TimerElapsed).await; + state_machine.handle(&Event::ButtonPressed).await; + state_machine.handle(&Event::TimerElapsed).await; + state_machine.handle(&Event::ButtonPressed).await; +} diff --git a/statig/src/awaitable/state_machine.rs b/statig/src/awaitable/state_machine.rs index 015918d..939fbe9 100644 --- a/statig/src/awaitable/state_machine.rs +++ b/statig/src/awaitable/state_machine.rs @@ -1,6 +1,7 @@ -use core::fmt::Debug; +use core::{fmt::Debug, future}; +use std::future::Future; -use super::awaitable; +use super::{awaitable, into_state_machine}; use crate::{Inner, IntoStateMachine}; /// A state machine where the shared storage is of type `Self`. @@ -9,6 +10,10 @@ where Self: Send, for<'sub> Self::Superstate<'sub>: awaitable::Superstate + Send, Self::State: awaitable::State + Send, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { /// Create a state machine that will be lazily initialized. fn state_machine(self) -> StateMachine @@ -34,14 +39,16 @@ where }; UninitializedStateMachine { inner } } -} -impl IntoStateMachineExt for T -where - Self: IntoStateMachine + Send, - for<'sub> Self::Superstate<'sub>: awaitable::Superstate + Send, - Self::State: awaitable::State + Send, -{ + #[cfg(feature = "async")] + fn on_transition( + &mut self, + _from: &Self::State, + _to: &Self::State, + ) -> impl Future + Send { + use std::task::Poll; + std::future::poll_fn(|_| Poll::Ready(())) + } } /// A state machine that will be lazily initialized. @@ -55,7 +62,7 @@ where impl StateMachine where - M: IntoStateMachine + Send, + M: IntoStateMachineExt + Send, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { @@ -233,21 +240,25 @@ where /// A state machine that has been initialized. pub struct InitializedStateMachine where - M: IntoStateMachine, + M: IntoStateMachineExt, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { inner: Inner, } impl InitializedStateMachine where - M: IntoStateMachine + Send, + M: IntoStateMachineExt + Send, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { /// Handle the given event. pub async fn handle(&mut self, event: &M::Event<'_>) where - for<'ctx> M: IntoStateMachine = ()>, + for<'ctx> M: IntoStateMachineExt = ()>, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { @@ -257,7 +268,7 @@ where /// Handle the given event. pub async fn handle_with_context(&mut self, event: &M::Event<'_>, context: &mut M::Context<'_>) where - M: IntoStateMachine, + M: IntoStateMachineExt, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { @@ -290,8 +301,12 @@ where impl Clone for InitializedStateMachine where - M: IntoStateMachine + Clone, + M: IntoStateMachineExt + Clone, M::State: Clone, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { fn clone(&self) -> Self { Self { @@ -302,8 +317,12 @@ where impl Debug for InitializedStateMachine where - M: IntoStateMachine + Debug, + M: IntoStateMachineExt + Debug, M::State: Debug, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("InitializedStateMachine") @@ -315,8 +334,12 @@ where impl PartialEq for InitializedStateMachine where - M: IntoStateMachine + PartialEq, + M: IntoStateMachineExt + PartialEq, M::State: PartialEq, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner @@ -325,14 +348,22 @@ where impl Eq for InitializedStateMachine where - M: IntoStateMachine + PartialEq + Eq, + M: IntoStateMachineExt + PartialEq + Eq, M::State: PartialEq + Eq, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { } impl core::ops::Deref for InitializedStateMachine where - M: IntoStateMachine, + M: IntoStateMachineExt, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { type Target = M; @@ -379,16 +410,24 @@ where /// execute all the entry actions into the initial state. pub struct UninitializedStateMachine where - M: IntoStateMachine, + M: IntoStateMachineExt, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { inner: Inner, } impl UninitializedStateMachine where - M: IntoStateMachine + Send, + M: IntoStateMachineExt + Send, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { /// Initialize the state machine by executing all entry actions towards /// the initial state. @@ -416,7 +455,7 @@ where /// ``` pub async fn init(self) -> InitializedStateMachine where - for<'ctx> M: IntoStateMachine = ()>, + for<'ctx> M: IntoStateMachineExt = ()>, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { @@ -462,8 +501,11 @@ where impl Clone for UninitializedStateMachine where - M: IntoStateMachine + Clone, + M: IntoStateMachineExt + Clone, M::State: Clone, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate + Send, + ::State: awaitable::state::State, { fn clone(&self) -> Self { Self { @@ -474,8 +516,12 @@ where impl Debug for UninitializedStateMachine where - M: IntoStateMachine + Debug, + M: IntoStateMachineExt + Debug, M::State: Debug, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("UnInitializedStateMachine") @@ -487,8 +533,12 @@ where impl PartialEq for UninitializedStateMachine where - M: IntoStateMachine + PartialEq, + M: IntoStateMachineExt + PartialEq, M::State: PartialEq, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner @@ -497,14 +547,22 @@ where impl Eq for UninitializedStateMachine where - M: IntoStateMachine + PartialEq + Eq, + M: IntoStateMachineExt + PartialEq + Eq, M::State: PartialEq + Eq, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { } impl core::ops::Deref for UninitializedStateMachine where - M: IntoStateMachine, + M: IntoStateMachineExt, + for<'sub> ::Superstate<'sub>: + awaitable::superstate::Superstate, + for<'sub> ::Superstate<'sub>: Send, + ::State: awaitable::state::State, { type Target = M; diff --git a/statig/src/inner.rs b/statig/src/inner.rs index 46abe02..98adb06 100644 --- a/statig/src/inner.rs +++ b/statig/src/inner.rs @@ -1,3 +1,4 @@ +use crate::awaitable::IntoStateMachineExt; #[cfg(feature = "async")] use crate::awaitable::{self, StateExt as _}; use crate::blocking::{self, StateExt as _}; @@ -58,7 +59,7 @@ where #[cfg(feature = "async")] impl Inner where - M: IntoStateMachine + Send, + M: IntoStateMachineExt + Send, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, M::State: awaitable::State + Send + 'static, @@ -106,6 +107,7 @@ where .await; M::ON_TRANSITION(&mut self.shared_storage, &target, &self.state); + M::on_transition(&mut self.shared_storage, &target, &self.state).await; } } From bacd5939265d4843cf543f49705fea012e5db4d6 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Mon, 21 Oct 2024 20:48:26 +0200 Subject: [PATCH 02/13] simplification --- statig/src/awaitable/state_machine.rs | 81 ++++----------------------- 1 file changed, 12 insertions(+), 69 deletions(-) diff --git a/statig/src/awaitable/state_machine.rs b/statig/src/awaitable/state_machine.rs index 939fbe9..268a3da 100644 --- a/statig/src/awaitable/state_machine.rs +++ b/statig/src/awaitable/state_machine.rs @@ -1,19 +1,15 @@ -use core::{fmt::Debug, future}; +use core::fmt::Debug; use std::future::Future; -use super::{awaitable, into_state_machine}; +use super::{awaitable, into_state_machine, State, Superstate}; use crate::{Inner, IntoStateMachine}; /// A state machine where the shared storage is of type `Self`. -pub trait IntoStateMachineExt: IntoStateMachine -where - Self: Send, - for<'sub> Self::Superstate<'sub>: awaitable::Superstate + Send, - Self::State: awaitable::State + Send, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, +pub trait IntoStateMachineExt: + for<'sub> IntoStateMachine< + Superstate<'sub>: Superstate + Send, + State: State + Send + 'static, + > + Send { /// Create a state machine that will be lazily initialized. fn state_machine(self) -> StateMachine @@ -241,17 +237,13 @@ where pub struct InitializedStateMachine where M: IntoStateMachineExt, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { inner: Inner, } impl InitializedStateMachine where - M: IntoStateMachineExt + Send, + M: IntoStateMachineExt, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { @@ -303,10 +295,6 @@ impl Clone for InitializedStateMachine where M: IntoStateMachineExt + Clone, M::State: Clone, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { fn clone(&self) -> Self { Self { @@ -319,10 +307,6 @@ impl Debug for InitializedStateMachine where M: IntoStateMachineExt + Debug, M::State: Debug, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("InitializedStateMachine") @@ -336,10 +320,6 @@ impl PartialEq for InitializedStateMachine where M: IntoStateMachineExt + PartialEq, M::State: PartialEq, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner @@ -348,22 +328,14 @@ where impl Eq for InitializedStateMachine where - M: IntoStateMachineExt + PartialEq + Eq, - M::State: PartialEq + Eq, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, + M: IntoStateMachineExt + Eq, + M::State: Eq, { } impl core::ops::Deref for InitializedStateMachine where M: IntoStateMachineExt, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { type Target = M; @@ -411,23 +383,13 @@ where pub struct UninitializedStateMachine where M: IntoStateMachineExt, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { inner: Inner, } impl UninitializedStateMachine where - M: IntoStateMachineExt + Send, - M::State: awaitable::State + 'static + Send, - for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, + M: IntoStateMachineExt, { /// Initialize the state machine by executing all entry actions towards /// the initial state. @@ -503,9 +465,6 @@ impl Clone for UninitializedStateMachine where M: IntoStateMachineExt + Clone, M::State: Clone, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate + Send, - ::State: awaitable::state::State, { fn clone(&self) -> Self { Self { @@ -518,10 +477,6 @@ impl Debug for UninitializedStateMachine where M: IntoStateMachineExt + Debug, M::State: Debug, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("UnInitializedStateMachine") @@ -535,10 +490,6 @@ impl PartialEq for UninitializedStateMachine where M: IntoStateMachineExt + PartialEq, M::State: PartialEq, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { fn eq(&self, other: &Self) -> bool { self.inner == other.inner @@ -548,21 +499,13 @@ where impl Eq for UninitializedStateMachine where M: IntoStateMachineExt + PartialEq + Eq, - M::State: PartialEq + Eq, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, + M::State: State + PartialEq + Eq, { } impl core::ops::Deref for UninitializedStateMachine where M: IntoStateMachineExt, - for<'sub> ::Superstate<'sub>: - awaitable::superstate::Superstate, - for<'sub> ::Superstate<'sub>: Send, - ::State: awaitable::state::State, { type Target = M; From 2d788dc7c3124cc7af734bccc24ac77e0975f9b1 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Mon, 21 Oct 2024 21:24:49 +0200 Subject: [PATCH 03/13] add async into trait --- examples/no_macro/blinky_async/src/main.rs | 20 +++++++++++++------- macro/src/codegen.rs | 2 ++ statig/src/awaitable/state_machine.rs | 21 +++++++++++++++------ statig/src/inner.rs | 3 ++- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/examples/no_macro/blinky_async/src/main.rs b/examples/no_macro/blinky_async/src/main.rs index a574922..0734714 100644 --- a/examples/no_macro/blinky_async/src/main.rs +++ b/examples/no_macro/blinky_async/src/main.rs @@ -43,20 +43,22 @@ impl IntoStateMachine for Blinky { /// The initial state of the state machine. const INITIAL: State = State::LedOn; - } -impl IntoStateMachineExt for Blinky { - +impl AsyncIntoStateMachine for Blinky { async fn on_transition(&mut self, from_state: &State, to_state: &State) { println!("Transitioned to {:?}", to_state); } - } // Implement the `statig::State` trait for the state enum. impl awaitable::State for State { - fn call_handler<'fut>(&'fut mut self, blinky: &'fut mut Blinky, event: &'fut Event, _: &'fut mut ()) -> Pin> + Send + 'fut)>>{ + fn call_handler<'fut>( + &'fut mut self, + blinky: &'fut mut Blinky, + event: &'fut Event, + _: &'fut mut (), + ) -> Pin> + Send + 'fut)>> { match self { State::LedOn => Box::pin(Blinky::led_on(event)), State::LedOff => Box::pin(Blinky::led_off(event)), @@ -75,7 +77,12 @@ impl awaitable::State for State { // Implement the `statig::Superstate` trait for the superstate enum. impl awaitable::Superstate for Superstate { - fn call_handler<'fut>(&'fut mut self, blinky: &'fut mut Blinky, event: &'fut Event, _: &'fut mut ()) -> Pin> + Send + 'fut)>> { + fn call_handler<'fut>( + &'fut mut self, + blinky: &'fut mut Blinky, + event: &'fut Event, + _: &'fut mut (), + ) -> Pin> + Send + 'fut)>> { Box::pin(match self { Superstate::Blinking => Blinky::blinking(event), }) @@ -110,7 +117,6 @@ impl Blinky { _ => Super, } } - } #[tokio::main] diff --git a/macro/src/codegen.rs b/macro/src/codegen.rs index 42d1a1b..3c7d448 100644 --- a/macro/src/codegen.rs +++ b/macro/src/codegen.rs @@ -86,6 +86,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { #on_dispatch } + // use statig::#mode::State; + // impl #impl_generics statig::#mode::IntoStateMachineExt for #shared_storage_type #where_clause {} ) } diff --git a/statig/src/awaitable/state_machine.rs b/statig/src/awaitable/state_machine.rs index 268a3da..e70b0e2 100644 --- a/statig/src/awaitable/state_machine.rs +++ b/statig/src/awaitable/state_machine.rs @@ -35,8 +35,17 @@ pub trait IntoStateMachineExt: }; UninitializedStateMachine { inner } } +} + +impl IntoStateMachineExt for T +where + T: IntoStateMachine + Send, + T::State: State + 'static, + for<'sub> T::Superstate<'sub>: Superstate + Send, +{ +} - #[cfg(feature = "async")] +pub trait AsyncIntoStateMachine: IntoStateMachineExt { fn on_transition( &mut self, _from: &Self::State, @@ -58,7 +67,7 @@ where impl StateMachine where - M: IntoStateMachineExt + Send, + M: AsyncIntoStateMachine, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { @@ -243,7 +252,7 @@ where impl InitializedStateMachine where - M: IntoStateMachineExt, + M: AsyncIntoStateMachine, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { @@ -260,7 +269,7 @@ where /// Handle the given event. pub async fn handle_with_context(&mut self, event: &M::Event<'_>, context: &mut M::Context<'_>) where - M: IntoStateMachineExt, + M: AsyncIntoStateMachine, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { @@ -389,7 +398,7 @@ where impl UninitializedStateMachine where - M: IntoStateMachineExt, + M: AsyncIntoStateMachine, { /// Initialize the state machine by executing all entry actions towards /// the initial state. @@ -417,7 +426,7 @@ where /// ``` pub async fn init(self) -> InitializedStateMachine where - for<'ctx> M: IntoStateMachineExt = ()>, + for<'ctx> M: AsyncIntoStateMachine = ()>, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { diff --git a/statig/src/inner.rs b/statig/src/inner.rs index 98adb06..a53dbc9 100644 --- a/statig/src/inner.rs +++ b/statig/src/inner.rs @@ -2,6 +2,7 @@ use crate::awaitable::IntoStateMachineExt; #[cfg(feature = "async")] use crate::awaitable::{self, StateExt as _}; use crate::blocking::{self, StateExt as _}; +use crate::prelude::AsyncIntoStateMachine; use crate::{IntoStateMachine, Response}; /// Private internal representation of a state machine that is used for the public types. @@ -59,7 +60,7 @@ where #[cfg(feature = "async")] impl Inner where - M: IntoStateMachineExt + Send, + M: AsyncIntoStateMachine, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, M::State: awaitable::State + Send + 'static, From d9d53446c12c6668c483ac0fb9f115bf0df8f089 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Tue, 22 Oct 2024 14:56:50 +0200 Subject: [PATCH 04/13] fix macros --- examples/no_macro/blinky_async/src/main.rs | 4 +--- statig/src/awaitable/state_machine.rs | 21 +++++---------------- statig/src/inner.rs | 5 ++--- statig/src/into_state_machine.rs | 11 +++++++++++ 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/examples/no_macro/blinky_async/src/main.rs b/examples/no_macro/blinky_async/src/main.rs index 0734714..06cc37f 100644 --- a/examples/no_macro/blinky_async/src/main.rs +++ b/examples/no_macro/blinky_async/src/main.rs @@ -43,10 +43,8 @@ impl IntoStateMachine for Blinky { /// The initial state of the state machine. const INITIAL: State = State::LedOn; -} -impl AsyncIntoStateMachine for Blinky { - async fn on_transition(&mut self, from_state: &State, to_state: &State) { + async fn on_transition_async(&mut self, from_state: &State, to_state: &State) { println!("Transitioned to {:?}", to_state); } } diff --git a/statig/src/awaitable/state_machine.rs b/statig/src/awaitable/state_machine.rs index e70b0e2..f9f135a 100644 --- a/statig/src/awaitable/state_machine.rs +++ b/statig/src/awaitable/state_machine.rs @@ -45,17 +45,6 @@ where { } -pub trait AsyncIntoStateMachine: IntoStateMachineExt { - fn on_transition( - &mut self, - _from: &Self::State, - _to: &Self::State, - ) -> impl Future + Send { - use std::task::Poll; - std::future::poll_fn(|_| Poll::Ready(())) - } -} - /// A state machine that will be lazily initialized. pub struct StateMachine where @@ -67,7 +56,7 @@ where impl StateMachine where - M: AsyncIntoStateMachine, + M: IntoStateMachineExt, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { @@ -252,7 +241,7 @@ where impl InitializedStateMachine where - M: AsyncIntoStateMachine, + M: IntoStateMachineExt, M::State: awaitable::State + 'static + Send, for<'sub> M::Superstate<'sub>: awaitable::Superstate + Send, { @@ -269,7 +258,7 @@ where /// Handle the given event. pub async fn handle_with_context(&mut self, event: &M::Event<'_>, context: &mut M::Context<'_>) where - M: AsyncIntoStateMachine, + M: IntoStateMachineExt, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { @@ -398,7 +387,7 @@ where impl UninitializedStateMachine where - M: AsyncIntoStateMachine, + M: IntoStateMachineExt, { /// Initialize the state machine by executing all entry actions towards /// the initial state. @@ -426,7 +415,7 @@ where /// ``` pub async fn init(self) -> InitializedStateMachine where - for<'ctx> M: AsyncIntoStateMachine = ()>, + for<'ctx> M: IntoStateMachineExt = ()>, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, { diff --git a/statig/src/inner.rs b/statig/src/inner.rs index a53dbc9..24cc702 100644 --- a/statig/src/inner.rs +++ b/statig/src/inner.rs @@ -2,7 +2,6 @@ use crate::awaitable::IntoStateMachineExt; #[cfg(feature = "async")] use crate::awaitable::{self, StateExt as _}; use crate::blocking::{self, StateExt as _}; -use crate::prelude::AsyncIntoStateMachine; use crate::{IntoStateMachine, Response}; /// Private internal representation of a state machine that is used for the public types. @@ -60,7 +59,7 @@ where #[cfg(feature = "async")] impl Inner where - M: AsyncIntoStateMachine, + M: IntoStateMachineExt, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, M::State: awaitable::State + Send + 'static, @@ -108,7 +107,7 @@ where .await; M::ON_TRANSITION(&mut self.shared_storage, &target, &self.state); - M::on_transition(&mut self.shared_storage, &target, &self.state).await; + M::on_transition_async(&mut self.shared_storage, &target, &self.state).await; } } diff --git a/statig/src/into_state_machine.rs b/statig/src/into_state_machine.rs index bffea31..3b4fbf1 100644 --- a/statig/src/into_state_machine.rs +++ b/statig/src/into_state_machine.rs @@ -1,3 +1,5 @@ +use core::future::Future; + use crate::StateOrSuperstate; /// Trait for transorming a type into a state machine. @@ -29,4 +31,13 @@ where /// Method that is called *after* every transition. const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; + + fn on_transition_async( + &mut self, + _from: &Self::State, + _to: &Self::State, + ) -> impl Future + Send { + use std::task::Poll; + std::future::poll_fn(|_| Poll::Ready(())) + } } From 21b64d90bc1be3b48957f690fec129926a7b6cf8 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Tue, 22 Oct 2024 15:12:47 +0200 Subject: [PATCH 05/13] convert async trait method to const fn pointer --- examples/no_macro/blinky_async/src/main.rs | 23 ++++++++++++++++++---- statig/src/inner.rs | 2 +- statig/src/into_state_machine.rs | 16 +++++++-------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/examples/no_macro/blinky_async/src/main.rs b/examples/no_macro/blinky_async/src/main.rs index 06cc37f..643a09b 100644 --- a/examples/no_macro/blinky_async/src/main.rs +++ b/examples/no_macro/blinky_async/src/main.rs @@ -1,7 +1,12 @@ #![allow(unused)] use statig::awaitable::{self, *}; -use std::{future::Future, io::Write, pin::Pin}; +use std::{ + future::{poll_fn, Future}, + io::Write, + pin::Pin, + task::Poll, +}; #[derive(Default)] pub struct Blinky; @@ -44,9 +49,19 @@ impl IntoStateMachine for Blinky { /// The initial state of the state machine. const INITIAL: State = State::LedOn; - async fn on_transition_async(&mut self, from_state: &State, to_state: &State) { - println!("Transitioned to {:?}", to_state); - } + const ON_DISPATCH: fn(&mut Self, StateOrSuperstate<'_, '_, Self>, &Self::Event<'_>) = + |_, _, _| {}; + + const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; + + const ON_TRANSITION_ASYNC: fn( + &mut Self, + _from: &Self::State, + _to: &Self::State, + ) -> Pin + Send>> = |_blinky, from, to| { + println!("transitioned from {:?} to {:?}", from, to); + Box::pin(poll_fn(|_| Poll::Ready(()))) + }; } // Implement the `statig::State` trait for the state enum. diff --git a/statig/src/inner.rs b/statig/src/inner.rs index 24cc702..4337b89 100644 --- a/statig/src/inner.rs +++ b/statig/src/inner.rs @@ -107,7 +107,7 @@ where .await; M::ON_TRANSITION(&mut self.shared_storage, &target, &self.state); - M::on_transition_async(&mut self.shared_storage, &target, &self.state).await; + M::ON_TRANSITION_ASYNC(&mut self.shared_storage, &target, &self.state).await; } } diff --git a/statig/src/into_state_machine.rs b/statig/src/into_state_machine.rs index 3b4fbf1..ddba50c 100644 --- a/statig/src/into_state_machine.rs +++ b/statig/src/into_state_machine.rs @@ -1,4 +1,4 @@ -use core::future::Future; +use core::{future::Future, pin::Pin}; use crate::StateOrSuperstate; @@ -32,12 +32,12 @@ where /// Method that is called *after* every transition. const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; - fn on_transition_async( - &mut self, - _from: &Self::State, - _to: &Self::State, - ) -> impl Future + Send { + const ON_TRANSITION_ASYNC: fn( + &mut Self, + from: &Self::State, + to: &Self::State, + ) -> Pin + Send>> = |_, _, _| { use std::task::Poll; - std::future::poll_fn(|_| Poll::Ready(())) - } + Box::pin(std::future::poll_fn(|_| Poll::Ready(()))) + }; } From 62fc056d45f6d4d857425778de3698198f5299de Mon Sep 17 00:00:00 2001 From: Andrew <135123941+andrew-otiv@users.noreply.github.com> Date: Wed, 23 Oct 2024 09:26:24 +0200 Subject: [PATCH 06/13] Apply suggestions from code review delete unnecessary comments --- examples/no_macro/blinky_async/Cargo.toml | 2 -- macro/src/codegen.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/examples/no_macro/blinky_async/Cargo.toml b/examples/no_macro/blinky_async/Cargo.toml index c656cce..032376f 100644 --- a/examples/no_macro/blinky_async/Cargo.toml +++ b/examples/no_macro/blinky_async/Cargo.toml @@ -3,8 +3,6 @@ name = "no_macro_blink_async" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] statig = { path = "../../../statig", features = ["async"] } tokio = { version = "*", features = ["full"] } diff --git a/macro/src/codegen.rs b/macro/src/codegen.rs index 3c7d448..42d1a1b 100644 --- a/macro/src/codegen.rs +++ b/macro/src/codegen.rs @@ -86,8 +86,6 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { #on_dispatch } - // use statig::#mode::State; - // impl #impl_generics statig::#mode::IntoStateMachineExt for #shared_storage_type #where_clause {} ) } From 5489e43fff1e63fab2d80444c8e5afc9d73d7f38 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 09:31:08 +0200 Subject: [PATCH 07/13] add test dependencies --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index b82329e..2a2ff67 100644 --- a/README.md +++ b/README.md @@ -494,6 +494,16 @@ Short answer: nothing. `#[state_machine]` simply parses the underlying `impl` bl I would say they serve a different purpose. The [typestate pattern](http://cliffle.com/blog/rust-typestate/) is very useful for designing an API as it is able to enforce the validity of operations at compile time by making each state a unique type. But `statig` is designed to model a dynamic system where events originate externally and the order of operations is determined at run time. More concretely, this means that the state machine is going to sit in a loop where events are read from a queue and submitted to the state machine using the `handle()` method. If we want to do the same with a state machine that uses the typestate pattern we'd have to use an enum to wrap all our different states and match events to operations on these states. This means extra boilerplate code for little advantage as the order of operations is unknown so it can't be checked at compile time. On the other hand `statig` gives you the ability to create a hierarchy of states which I find to be invaluable as state machines grow in complexity. +## Testing + +Install the following dependencies: + +```sh +sudo apt install cmake libfontconfig1-dev +cargo test --workspace +``` + + --- ## Credits From 4ac34e69b2f5ee698b98e8590a5aba3a443362a4 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 10:29:08 +0200 Subject: [PATCH 08/13] add macro attribute for async on_transition --- examples/macro/async_blinky/src/main.rs | 14 ++++++++++++++ macro/src/analyze.rs | 13 +++++++++++++ macro/src/codegen.rs | 9 +++++++++ macro/src/lower.rs | 7 +++++++ 4 files changed, 43 insertions(+) diff --git a/examples/macro/async_blinky/src/main.rs b/examples/macro/async_blinky/src/main.rs index 4d0eb9a..44ab46b 100644 --- a/examples/macro/async_blinky/src/main.rs +++ b/examples/macro/async_blinky/src/main.rs @@ -1,9 +1,12 @@ #![allow(unused)] use futures::executor; +use futures::future::poll_fn; use statig::prelude::*; use std::fmt::Debug; +use std::future::Future; use std::io::Write; +use std::pin::Pin; use std::thread::spawn; #[derive(Debug, Default)] @@ -29,6 +32,8 @@ pub enum Event { superstate(derive(Debug)), // Set the `on_transition` callback. on_transition = "Self::on_transition", + // Set the `on_transition_async` callback. + on_transition_async = "Self::on_transition_async", // Set the `on_dispatch` callback. on_dispatch = "Self::on_dispatch" )] @@ -83,6 +88,15 @@ impl Blinky { println!("transitioned from `{source:?}` to `{target:?}`"); } + fn on_transition_async( + &mut self, + source: &State, + target: &State, + ) -> Pin + Send + 'static>> { + println!("transitioned async from `{source:?}` to `{target:?}`"); + Box::pin(poll_fn(|_| std::task::Poll::Ready(()))) + } + fn on_dispatch(&mut self, state: StateOrSuperstate, event: &Event) { println!("dispatching `{event:?}` to `{state:?}`"); } diff --git a/macro/src/analyze.rs b/macro/src/analyze.rs index 1e1f90d..d3d0932 100644 --- a/macro/src/analyze.rs +++ b/macro/src/analyze.rs @@ -50,6 +50,8 @@ pub struct StateMachine { pub visibility: Visibility, /// Optional `on_transition` callback. pub on_transition: Option, + /// Optional `on_transition_async` callback. + pub on_transition_async: Option, /// Optional `on_dispatch` callback. pub on_dispatch: Option, } @@ -180,6 +182,7 @@ pub fn analyze_state_machine(attribute_args: &AttributeArgs, item_impl: &ItemImp let mut superstate_derives = Vec::new(); let mut on_transition = None; + let mut on_transition_async = None; let mut on_dispatch = None; let mut visibility = parse_quote!(pub); @@ -224,6 +227,14 @@ pub fn analyze_state_machine(attribute_args: &AttributeArgs, item_impl: &ItemImp _ => abort!(name_value, "must be a string literal"), } } + NestedMeta::Meta(Meta::NameValue(name_value)) + if name_value.path.is_ident("on_transition_async") => + { + on_transition_async = match &name_value.lit { + Lit::Str(input_pat) => Some(input_pat.parse().unwrap()), + _ => abort!(name_value, "must be a string literal"), + } + } NestedMeta::Meta(Meta::NameValue(name_value)) if name_value.path.is_ident("on_dispatch") => { @@ -341,6 +352,7 @@ pub fn analyze_state_machine(attribute_args: &AttributeArgs, item_impl: &ItemImp superstate_derives, on_dispatch, on_transition, + on_transition_async, event_ident, context_ident, visibility, @@ -660,6 +672,7 @@ fn valid_state_analyze() { superstate_ident, superstate_derives, on_transition, + on_transition_async, on_dispatch, event_ident, context_ident, diff --git a/macro/src/codegen.rs b/macro/src/codegen.rs index 42d1a1b..3cd442c 100644 --- a/macro/src/codegen.rs +++ b/macro/src/codegen.rs @@ -66,6 +66,13 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { ), }; + let on_transition_async = match &ir.state_machine.on_transition_async { + None => quote!(), + Some(on_transition_async) => quote!( + const ON_TRANSITION_ASYNC: fn(&mut Self, &Self::State, &Self::State) -> core::pin::Pin + Send>> = #on_transition_async; + ), + }; + let on_dispatch = match &ir.state_machine.on_dispatch { None => quote!(), Some(on_dispatch) => quote!( @@ -84,6 +91,8 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { #on_transition + #on_transition_async + #on_dispatch } ) diff --git a/macro/src/lower.rs b/macro/src/lower.rs index d32a1da..84314d6 100644 --- a/macro/src/lower.rs +++ b/macro/src/lower.rs @@ -58,6 +58,8 @@ pub struct StateMachine { pub superstate_generics: Generics, /// The path of the `on_transition` callback. pub on_transition: Option, + /// The path of the `on_transition_async` callback. + pub on_transition_async: Option, /// The path of the `on_dispatch` callback. pub on_dispatch: Option, /// The visibility for the derived types, @@ -137,6 +139,8 @@ pub fn lower(model: &Model) -> Ir { let state_ident = model.state_machine.state_ident.clone(); let superstate_ident = model.state_machine.superstate_ident.clone(); let on_transition = model.state_machine.on_transition.clone(); + + let on_transition_async = model.state_machine.on_transition_async.clone(); let on_dispatch = model.state_machine.on_dispatch.clone(); let event_ident = model.state_machine.event_ident.clone(); let context_ident = model.state_machine.context_ident.clone(); @@ -421,6 +425,7 @@ pub fn lower(model: &Model) -> Ir { superstate_derives, superstate_generics, on_transition, + on_transition_async, on_dispatch, visibility, event_ident, @@ -708,6 +713,7 @@ fn create_analyze_state_machine() -> analyze::StateMachine { superstate_ident: parse_quote!(Superstate), superstate_derives: vec![parse_quote!(Copy), parse_quote!(Clone)], on_transition: None, + on_transition_async: None, on_dispatch: None, visibility: parse_quote!(pub), event_ident: parse_quote!(input), @@ -733,6 +739,7 @@ fn create_lower_state_machine() -> StateMachine { superstate_derives: vec![parse_quote!(Copy), parse_quote!(Clone)], superstate_generics, on_transition: None, + on_transition_async: None, on_dispatch: None, visibility: parse_quote!(pub), event_ident: parse_quote!(input), From 4d8c8b7b6f9f4c90955fb3828ae21b75686a186e Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 11:10:32 +0200 Subject: [PATCH 09/13] fix lifetime issues with boxed future --- examples/macro/async_blinky/src/main.rs | 16 ++++++++++------ examples/no_macro/blinky_async/src/main.rs | 15 +++++++++------ macro/src/codegen.rs | 3 +-- statig/src/into_state_machine.rs | 10 +++++----- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/examples/macro/async_blinky/src/main.rs b/examples/macro/async_blinky/src/main.rs index 44ab46b..9f35e5e 100644 --- a/examples/macro/async_blinky/src/main.rs +++ b/examples/macro/async_blinky/src/main.rs @@ -88,13 +88,17 @@ impl Blinky { println!("transitioned from `{source:?}` to `{target:?}`"); } - fn on_transition_async( - &mut self, - source: &State, - target: &State, - ) -> Pin + Send + 'static>> { + async fn transitioning(&mut self, from: &State, to: &State) { + println!("transitioning from {:?} to {:?}", from, to); + } + + fn on_transition_async<'a>( + &'a mut self, + source: &'a State, + target: &'a State, + ) -> Pin + Send + 'a>> { println!("transitioned async from `{source:?}` to `{target:?}`"); - Box::pin(poll_fn(|_| std::task::Poll::Ready(()))) + Box::pin(self.transitioning(source, target)) } fn on_dispatch(&mut self, state: StateOrSuperstate, event: &Event) { diff --git a/examples/no_macro/blinky_async/src/main.rs b/examples/no_macro/blinky_async/src/main.rs index 643a09b..9c5315b 100644 --- a/examples/no_macro/blinky_async/src/main.rs +++ b/examples/no_macro/blinky_async/src/main.rs @@ -54,13 +54,13 @@ impl IntoStateMachine for Blinky { const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; - const ON_TRANSITION_ASYNC: fn( - &mut Self, - _from: &Self::State, - _to: &Self::State, - ) -> Pin + Send>> = |_blinky, from, to| { + const ON_TRANSITION_ASYNC: for<'a> fn( + &'a mut Self, + _from: &'a Self::State, + _to: &'a Self::State, + ) -> Pin + Send + 'a>> = |_blinky, from, to| { println!("transitioned from {:?} to {:?}", from, to); - Box::pin(poll_fn(|_| Poll::Ready(()))) + Box::pin(_blinky.transitioning(from, to)) }; } @@ -103,6 +103,9 @@ impl awaitable::Superstate for Superstate { } impl Blinky { + async fn transitioning(&mut self, from: &State, to: &State) { + println!("transitioned from {:?} to {:?}", from, to); + } async fn led_on(event: &Event) -> Response { match event { Event::TimerElapsed => Transition(State::LedOff), diff --git a/macro/src/codegen.rs b/macro/src/codegen.rs index 3cd442c..118bd75 100644 --- a/macro/src/codegen.rs +++ b/macro/src/codegen.rs @@ -37,7 +37,6 @@ pub fn codegen(ir: Ir) -> TokenStream { #superstate_impl ) } - fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { let shared_storage_type = &ir.state_machine.shared_storage_type; let (impl_generics, _, where_clause) = @@ -69,7 +68,7 @@ fn codegen_state_machine_impl(ir: &Ir) -> ItemImpl { let on_transition_async = match &ir.state_machine.on_transition_async { None => quote!(), Some(on_transition_async) => quote!( - const ON_TRANSITION_ASYNC: fn(&mut Self, &Self::State, &Self::State) -> core::pin::Pin + Send>> = #on_transition_async; + const ON_TRANSITION_ASYNC: for <'a> fn(&'a mut Self, &'a Self::State, &'a Self::State) -> core::pin::Pin + Send + 'a>> = #on_transition_async; ), }; diff --git a/statig/src/into_state_machine.rs b/statig/src/into_state_machine.rs index ddba50c..5329990 100644 --- a/statig/src/into_state_machine.rs +++ b/statig/src/into_state_machine.rs @@ -32,11 +32,11 @@ where /// Method that is called *after* every transition. const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; - const ON_TRANSITION_ASYNC: fn( - &mut Self, - from: &Self::State, - to: &Self::State, - ) -> Pin + Send>> = |_, _, _| { + const ON_TRANSITION_ASYNC: for<'a> fn( + &'a mut Self, + from: &'a Self::State, + to: &'a Self::State, + ) -> Pin + Send + 'a>> = |_, _, _| { use std::task::Poll; Box::pin(std::future::poll_fn(|_| Poll::Ready(()))) }; From 38571bddaed08d1844eed7bce7986d5ce0d0c9cf Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 11:27:13 +0200 Subject: [PATCH 10/13] improve naming --- statig/src/into_state_machine.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/statig/src/into_state_machine.rs b/statig/src/into_state_machine.rs index 5329990..b37e1bf 100644 --- a/statig/src/into_state_machine.rs +++ b/statig/src/into_state_machine.rs @@ -32,11 +32,12 @@ where /// Method that is called *after* every transition. const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; - const ON_TRANSITION_ASYNC: for<'a> fn( - &'a mut Self, - from: &'a Self::State, - to: &'a Self::State, - ) -> Pin + Send + 'a>> = |_, _, _| { + const ON_TRANSITION_ASYNC: for<'fut> fn( + &'fut mut Self, + from: &'fut Self::State, + to: &'fut Self::State, + ) + -> Pin + Send + 'fut>> = |_, _, _| { use std::task::Poll; Box::pin(std::future::poll_fn(|_| Poll::Ready(()))) }; From 1c842efeb6be1bccf5154d997a0d546ab7112f7f Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 11:27:39 +0200 Subject: [PATCH 11/13] improve naming of blinky async no macro --- examples/no_macro/blinky_async/src/main.rs | 50 ++++++++++++---------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/examples/no_macro/blinky_async/src/main.rs b/examples/no_macro/blinky_async/src/main.rs index 9c5315b..e9c7deb 100644 --- a/examples/no_macro/blinky_async/src/main.rs +++ b/examples/no_macro/blinky_async/src/main.rs @@ -9,7 +9,9 @@ use std::{ }; #[derive(Default)] -pub struct Blinky; +pub struct Blinky { + field: String, +} // The event that will be handled by the state machine. pub enum Event { @@ -49,18 +51,14 @@ impl IntoStateMachine for Blinky { /// The initial state of the state machine. const INITIAL: State = State::LedOn; - const ON_DISPATCH: fn(&mut Self, StateOrSuperstate<'_, '_, Self>, &Self::Event<'_>) = - |_, _, _| {}; - - const ON_TRANSITION: fn(&mut Self, &Self::State, &Self::State) = |_, _, _| {}; - - const ON_TRANSITION_ASYNC: for<'a> fn( - &'a mut Self, - _from: &'a Self::State, - _to: &'a Self::State, - ) -> Pin + Send + 'a>> = |_blinky, from, to| { + const ON_TRANSITION_ASYNC: for<'fut> fn( + &'fut mut Self, + &'fut Self::State, + &'fut Self::State, + ) + -> Pin + Send + 'fut>> = |blinky, from, to| { println!("transitioned from {:?} to {:?}", from, to); - Box::pin(_blinky.transitioning(from, to)) + Box::pin(blinky.transition_and_print_internal_state(from, to)) }; } @@ -73,9 +71,9 @@ impl awaitable::State for State { _: &'fut mut (), ) -> Pin> + Send + 'fut)>> { match self { - State::LedOn => Box::pin(Blinky::led_on(event)), - State::LedOff => Box::pin(Blinky::led_off(event)), - State::NotBlinking => Box::pin(Blinky::not_blinking(event)), + State::LedOn => Box::pin(Blinky::timer_elapsed_turn_off(event)), + State::LedOff => Box::pin(Blinky::timer_elapsed_turn_on(event)), + State::NotBlinking => Box::pin(Blinky::not_blinking_button_pressed(event)), } } @@ -97,37 +95,40 @@ impl awaitable::Superstate for Superstate { _: &'fut mut (), ) -> Pin> + Send + 'fut)>> { Box::pin(match self { - Superstate::Blinking => Blinky::blinking(event), + Superstate::Blinking => Blinky::blinking_button_pressed(event), }) } } impl Blinky { - async fn transitioning(&mut self, from: &State, to: &State) { - println!("transitioned from {:?} to {:?}", from, to); + async fn transition_and_print_internal_state(&mut self, from: &State, to: &State) { + println!( + "transitioned (current test value is: {}) from {:?} to {:?}", + self.field, from, to + ); } - async fn led_on(event: &Event) -> Response { + async fn timer_elapsed_turn_off(event: &Event) -> Response { match event { Event::TimerElapsed => Transition(State::LedOff), _ => Super, } } - async fn led_off(event: &Event) -> Response { + async fn timer_elapsed_turn_on(event: &Event) -> Response { match event { Event::TimerElapsed => Transition(State::LedOn), _ => Super, } } - async fn blinking(event: &Event) -> Response { + async fn blinking_button_pressed(event: &Event) -> Response { match event { Event::ButtonPressed => Transition(State::NotBlinking), _ => Super, } } - async fn not_blinking(event: &Event) -> Response { + async fn not_blinking_button_pressed(event: &Event) -> Response { match event { Event::ButtonPressed => Transition(State::LedOn), _ => Super, @@ -137,7 +138,10 @@ impl Blinky { #[tokio::main] async fn main() { - let mut state_machine = Blinky::default().state_machine(); + let mut state_machine = Blinky { + field: "test field value".to_string(), + } + .state_machine(); state_machine.handle(&Event::TimerElapsed).await; state_machine.handle(&Event::ButtonPressed).await; From 67635c3d29f6a6546760652fb7dd284ddbed9fb6 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 13:48:50 +0200 Subject: [PATCH 12/13] remove static --- macro/src/analyze.rs | 2 +- statig/src/awaitable/state_machine.rs | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/macro/src/analyze.rs b/macro/src/analyze.rs index d3d0932..50c955b 100644 --- a/macro/src/analyze.rs +++ b/macro/src/analyze.rs @@ -672,7 +672,7 @@ fn valid_state_analyze() { superstate_ident, superstate_derives, on_transition, - on_transition_async, + on_transition_async: None, on_dispatch, event_ident, context_ident, diff --git a/statig/src/awaitable/state_machine.rs b/statig/src/awaitable/state_machine.rs index f9f135a..94ac709 100644 --- a/statig/src/awaitable/state_machine.rs +++ b/statig/src/awaitable/state_machine.rs @@ -1,15 +1,11 @@ use core::fmt::Debug; -use std::future::Future; -use super::{awaitable, into_state_machine, State, Superstate}; +use super::{awaitable, State, Superstate}; use crate::{Inner, IntoStateMachine}; /// A state machine where the shared storage is of type `Self`. pub trait IntoStateMachineExt: - for<'sub> IntoStateMachine< - Superstate<'sub>: Superstate + Send, - State: State + Send + 'static, - > + Send + for<'sub> IntoStateMachine: Superstate + Send, State: State> + Send { /// Create a state machine that will be lazily initialized. fn state_machine(self) -> StateMachine @@ -37,11 +33,8 @@ pub trait IntoStateMachineExt: } } -impl IntoStateMachineExt for T -where - T: IntoStateMachine + Send, - T::State: State + 'static, - for<'sub> T::Superstate<'sub>: Superstate + Send, +impl IntoStateMachineExt for T where + T: for<'sub> IntoStateMachine, Superstate<'sub>: Superstate + Send> { } @@ -418,6 +411,7 @@ where for<'ctx> M: IntoStateMachineExt = ()>, for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, + M::State: Send + 'static, { let mut state_machine = InitializedStateMachine { inner: self.inner }; state_machine.inner.async_init_with_context(&mut ()).await; @@ -452,6 +446,7 @@ where where for<'evt> M::Event<'evt>: Send + Sync, for<'ctx> M::Context<'ctx>: Send + Sync, + M::State: Send + 'static, { let mut state_machine = InitializedStateMachine { inner: self.inner }; state_machine.inner.async_init_with_context(context).await; From cb6f127792370f1669e14aa52f00cce3620581e1 Mon Sep 17 00:00:00 2001 From: Willem Vanhulle Date: Wed, 23 Oct 2024 13:51:18 +0200 Subject: [PATCH 13/13] add send bound to blanket implementation --- statig/src/awaitable/state_machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statig/src/awaitable/state_machine.rs b/statig/src/awaitable/state_machine.rs index 94ac709..9fad851 100644 --- a/statig/src/awaitable/state_machine.rs +++ b/statig/src/awaitable/state_machine.rs @@ -34,7 +34,7 @@ pub trait IntoStateMachineExt: } impl IntoStateMachineExt for T where - T: for<'sub> IntoStateMachine, Superstate<'sub>: Superstate + Send> + T: for<'sub> IntoStateMachine, Superstate<'sub>: Superstate + Send> + Send { }