Skip to content

Commit

Permalink
Add support for async on no_std
Browse files Browse the repository at this point in the history
  • Loading branch information
mdeloof committed Jan 2, 2025
1 parent 3a7ba1e commit cce10a4
Show file tree
Hide file tree
Showing 21 changed files with 313 additions and 391 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ members = [
"examples/no_macro/history",
"examples/no_macro/calculator",
]
resolver = "2"
24 changes: 14 additions & 10 deletions examples/macro/async_blinky/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub enum Event {
/// `statig::Superstate` traits.
#[state_machine(
// This sets the initial state to `led_on`.
initial = "State::led_on()",
initial = "State::led_on(2)",
// Derive the Debug trait on the `State` enum.
state(derive(Debug)),
// Derive the Debug trait on the `Superstate` enum.
Expand All @@ -39,27 +39,27 @@ impl Blinky {
/// `event` argument will map to the event handler by the state machine.
/// Every state must return a `Response<State>`.
#[state(superstate = "blinking", entry_action = "cool")]
async fn led_on(event: &Event) -> Response<State> {
async fn led_on(value: &i32, event: &Event) -> Response<State> {
match event {
// When we receive a `TimerElapsed` event we transition to the `led_off` state.
Event::TimerElapsed => Transition(State::led_off()),
Event::TimerElapsed => Transition(State::led_off(2)),
// Other events are deferred to the superstate, in this case `blinking`.
_ => Super,
}
}

/// Note you can mix sync and async handlers/actions.
#[state(superstate = "blinking")]
fn led_off(event: &Event) -> Response<State> {
fn led_off(value: &i32, event: &Event) -> Response<State> {
match event {
Event::TimerElapsed => Transition(State::led_on()),
Event::TimerElapsed => Transition(State::led_on(2)),
_ => Super,
}
}

/// The `#[superstate]` attribute marks this as a superstate handler.
#[superstate]
async fn blinking(event: &Event) -> Response<State> {
async fn blinking(value: &i32, event: &Event) -> Response<State> {
match event {
Event::ButtonPressed => Transition(State::not_blinking()),
_ => Super,
Expand All @@ -69,7 +69,7 @@ impl Blinky {
#[state]
async fn not_blinking(event: &Event) -> Response<State> {
match event {
Event::ButtonPressed => Transition(State::led_on()),
Event::ButtonPressed => Transition(State::led_on(2)),
// Altough this state has no superstate, we can still defer the event which
// will cause the event to be handled by an implicit `top` superstate.
_ => Super,
Expand All @@ -90,16 +90,20 @@ impl Blinky {

#[tokio::main]
async fn main() {
let future = async {
let mut state_machine = Blinky::default().uninitialized_state_machine().init().await;
use tokio::task;

let future = async move {
let mut state_machine = Blinky.state_machine(); //.uninitialized_state_machine().init().await;

state_machine.handle(&Event::TimerElapsed).await;
state_machine.handle(&Event::ButtonPressed).await;
state_machine.handle(&Event::TimerElapsed).await;
state_machine.handle(&Event::ButtonPressed).await;
};

let handle = tokio::spawn(future);
let local = task::LocalSet::new();

let handle = local.run_until(future);

handle.await;
}
2 changes: 1 addition & 1 deletion examples/macro/barrier/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Foo {
}

fn main() {
let mut state_machine = Foo::default().uninitialized_state_machine().init();
let mut state_machine = Foo.uninitialized_state_machine().init();

state_machine.handle(&Event::A);
state_machine.handle(&Event::B);
Expand Down
2 changes: 1 addition & 1 deletion examples/macro/bench/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl CdPlayer {
}

fn main() {
let mut state_machine = CdPlayer::default().uninitialized_state_machine().init();
let mut state_machine = CdPlayer.uninitialized_state_machine().init();

let loops: u32 = rand::random();

Expand Down
2 changes: 1 addition & 1 deletion examples/macro/blinky/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl Blinky {
fn main() {
let start = std::time::Instant::now();

let mut state_machine = Blinky::default().state_machine();
let mut state_machine = Blinky.state_machine();

state_machine.handle(&Event::TimerElapsed);
state_machine.handle(&Event::ButtonPressed);
Expand Down
2 changes: 1 addition & 1 deletion examples/no_macro/bench/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl CdPlayer {
}

fn main() {
let mut state_machine = CdPlayer::default().uninitialized_state_machine().init();
let mut state_machine = CdPlayer.uninitialized_state_machine().init();

let loops: u32 = rand::random();

Expand Down
2 changes: 1 addition & 1 deletion examples/no_macro/blinky/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl Blinky {
}

fn main() {
let mut state_machine = Blinky::default().state_machine();
let mut state_machine = Blinky.state_machine();

state_machine.handle(&Event::TimerElapsed);
state_machine.handle(&Event::ButtonPressed);
Expand Down
2 changes: 1 addition & 1 deletion macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ syn = { version = "1.0.107", features = [
"visit-mut",
] }
quote = "1.0.23"
proc-macro2 = "1.0.50"
proc-macro2 = "1.0.78"
proc-macro-error = "1.0.4"
120 changes: 58 additions & 62 deletions macro/src/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
parse_quote, Arm, GenericParam, ItemEnum, ItemFn, ItemImpl, Lifetime, LifetimeDef, Variant,
};
use syn::{parse_quote, Arm, ItemEnum, ItemFn, ItemImpl, Lifetime, Variant};

use crate::lower::{Ir, Mode};
use crate::{CONTEXT_LIFETIME, EVENT_LIFETIME, SUPERSTATE_LIFETIME};
Expand Down Expand Up @@ -129,8 +127,7 @@ fn codegen_state_impl(ir: &Ir) -> ItemImpl {

fn codegen_state_impl_state(ir: &Ir) -> ItemImpl {
let shared_storage_type = &ir.state_machine.shared_storage_type;
let (impl_generics, _, where_clause) =
&ir.state_machine.shared_storage_generics.split_for_impl();
let (impl_generics, _, where_clause) = &ir.state_machine.state_impl_generics.split_for_impl();
let state_ident = &ir.state_machine.state_ident;
let (_, state_generics, _) = &ir.state_machine.state_generics.split_for_impl();
let event_ident = &ir.state_machine.event_ident;
Expand Down Expand Up @@ -212,43 +209,47 @@ fn codegen_state_impl_state(ir: &Ir) -> ItemImpl {
#[allow(unused)]
impl #impl_generics statig::awaitable::State<#shared_storage_type> for #state_ident #state_generics #where_clause
{
fn call_handler<'fut>(
&'fut mut self,
shared_storage: &'fut mut #shared_storage_type,
#event_ident: &'fut <#shared_storage_type as statig::IntoStateMachine>::Event<'_>,
#context_ident: &'fut mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> core::pin::Pin<std::boxed::Box<dyn core::future::Future<Output = statig::Response<Self>> + 'fut + Send>> {
Box::pin(async move {
#[allow(clippy::manual_async_fn)]
fn call_handler(
&mut self,
shared_storage: &mut #shared_storage_type,
#event_ident: &<#shared_storage_type as statig::IntoStateMachine>::Event<'_>,
#context_ident: &mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> impl core::future::Future<Output = statig::Response<Self>> {
async move {
match self {
#(#call_handler_arms),*
}
})
}
}

fn call_entry_action<'fut>(
&'fut mut self,
shared_storage: &'fut mut #shared_storage_type,
#context_ident: &'fut mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> core::pin::Pin<std::boxed::Box<dyn core::future::Future<Output = ()> + 'fut + Send>> {
Box::pin(async move {
#[allow(clippy::manual_async_fn)]
fn call_entry_action(
&mut self,
shared_storage: &mut #shared_storage_type,
#context_ident: &mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> impl core::future::Future<Output = ()> {
async move {
match self {
#(#call_entry_action_arms),*
}
})
}
}

fn call_exit_action<'fut>(
&'fut mut self,
shared_storage: &'fut mut #shared_storage_type,
#context_ident: &'fut mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> core::pin::Pin<std::boxed::Box<dyn core::future::Future<Output = ()> + 'fut + Send>> {
Box::pin(async move {
#[allow(clippy::manual_async_fn)]
fn call_exit_action(
&mut self,
shared_storage: &mut #shared_storage_type,
#context_ident: &mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> impl core::future::Future<Output = ()> {
async move {
match self {
#(#call_exit_action_arms),*
}
})
}
}

#[allow(clippy::manual_async_fn)]
fn superstate(&mut self) -> Option<<#shared_storage_type as statig::IntoStateMachine>::Superstate<'_>> {
match self {
#(#superstate_arms),*
Expand Down Expand Up @@ -281,18 +282,9 @@ fn codegen_superstate(ir: &Ir) -> ItemEnum {

fn codegen_superstate_impl_superstate(ir: &Ir) -> ItemImpl {
let shared_storage_type = &ir.state_machine.shared_storage_type;
let mut shared_storage_generics = ir.state_machine.shared_storage_generics.clone();
let lifetime = Lifetime::new(SUPERSTATE_LIFETIME, Span::call_site());
let superstate_lifetime_def = LifetimeDef::new(lifetime.clone());
let superstate_lifetime_param = GenericParam::Lifetime(superstate_lifetime_def);
shared_storage_generics
.params
.push(superstate_lifetime_param);
match &mut shared_storage_generics.where_clause {
Some(clause) => clause.predicates.push(parse_quote!(Self: #lifetime)),
None => shared_storage_generics.where_clause = parse_quote!(where Self: #lifetime),
}
let (impl_generics, _, where_clause) = shared_storage_generics.split_for_impl();

let (impl_generics, _, where_clause) =
ir.state_machine.superstate_impl_generics.split_for_impl();
let superstate_ident = &ir.state_machine.superstate_ident;
let (_, superstate_generics, _) = &ir.state_machine.superstate_generics.split_for_impl();
let event_ident = &ir.state_machine.event_ident;
Expand Down Expand Up @@ -373,44 +365,48 @@ fn codegen_superstate_impl_superstate(ir: &Ir) -> ItemImpl {
#[allow(unused)]
impl #impl_generics statig::awaitable::Superstate<#shared_storage_type> for #superstate_ident #superstate_generics #where_clause
{
fn call_handler<'fut>(
&'fut mut self,
shared_storage: &'fut mut #shared_storage_type,
#event_ident: &'fut <#shared_storage_type as statig::IntoStateMachine>::Event<'_>,
#context_ident: &'fut mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> core::pin::Pin<std::boxed::Box<dyn core::future::Future<Output = statig::Response<<#shared_storage_type as statig::IntoStateMachine>::State>> + 'fut + Send>> {
Box::pin(async move {
#[allow(clippy::manual_async_fn)]
fn call_handler(
&mut self,
shared_storage: &mut #shared_storage_type,
#event_ident: &<#shared_storage_type as statig::IntoStateMachine>::Event<'_>,
#context_ident: &mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> impl core::future::Future<Output = statig::Response<<#shared_storage_type as statig::IntoStateMachine>::State>> {
async move {
match self {
#(#call_handler_arms),*
}
})
}
}

fn call_entry_action<'fut>(
&'fut mut self,
shared_storage: &'fut mut #shared_storage_type,
#context_ident: &'fut mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> core::pin::Pin<std::boxed::Box<dyn core::future::Future<Output = ()> + 'fut + Send>> {
Box::pin(async move {
#[allow(clippy::manual_async_fn)]
fn call_entry_action(
&mut self,
shared_storage: &mut #shared_storage_type,
#context_ident: &mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> impl core::future::Future<Output = ()> {
async move {
match self {
#(#call_entry_action_arms),*
}
})
}
}

fn call_exit_action<'fut>(
&'fut mut self,
shared_storage: &'fut mut #shared_storage_type,
#context_ident: &'fut mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> core::pin::Pin<std::boxed::Box<dyn core::future::Future<Output = ()> + 'fut + Send>> {
Box::pin(async move {
#[allow(clippy::manual_async_fn)]
fn call_exit_action(
&mut self,
shared_storage: &mut #shared_storage_type,
#context_ident: &mut <#shared_storage_type as statig::IntoStateMachine>::Context<'_>
) -> impl core::future::Future<Output = ()> {
async move {
match self {
#(#call_exit_action_arms),*
}
})
}
}

fn superstate(&mut self) -> Option<<#shared_storage_type as statig::IntoStateMachine>::Superstate<'_>> {
#[allow(clippy::manual_async_fn)]
fn superstate(&mut self) -> Option<Self> {
match self {
#(#superstate_arms),*
}
Expand Down
24 changes: 21 additions & 3 deletions macro/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ pub struct StateMachine {
pub state_derives: Vec<Path>,
/// The generics associated with the state type.
pub state_generics: Generics,
/// The generics associated with the state trait implementation
pub state_impl_generics: Generics,
/// The type of the superstate enum (ex. `Superstate<'sub>`)
pub superstate_ident: Ident,
/// Derives that will be applied to the superstate type.
pub superstate_derives: Vec<Path>,
/// The generics associated with the superstate type.
pub superstate_generics: Generics,
/// The generics associated with the superstate trait implementation
pub superstate_impl_generics: Generics,
/// The path of the `on_transition` callback.
pub on_transition: Option<Path>,
/// The path of the `on_dispatch` callback.
Expand Down Expand Up @@ -385,6 +389,8 @@ pub fn lower(model: &Model) -> Ir {
}
}

let state_impl_generics = shared_storage_generics.clone();

let mut superstate_generics = Generics::default();
for (param, predicates) in &shared_storage_generics_map {
if superstate_generic_params.contains(param) {
Expand All @@ -401,11 +407,17 @@ pub fn lower(model: &Model) -> Ir {
}
}

let mut superstate_impl_generics = shared_storage_generics.clone();

// If a lifetime is required it must be part of the superstate generics.
if let Some(lifetime) = superstate_lifetime {
superstate_generics
.params
.push(GenericParam::Lifetime(syn::LifetimeDef::new(lifetime)));
let lifetime_generic = GenericParam::Lifetime(syn::LifetimeDef::new(lifetime.clone()));
superstate_generics.params.push(lifetime_generic.clone());
superstate_impl_generics.params.push(lifetime_generic);
match &mut superstate_impl_generics.where_clause {
Some(clause) => clause.predicates.push(parse_quote!(Self: #lifetime)),
None => superstate_impl_generics.where_clause = parse_quote!(where Self: #lifetime),
}
}

let state_machine = StateMachine {
Expand All @@ -417,9 +429,11 @@ pub fn lower(model: &Model) -> Ir {
state_ident,
state_derives,
state_generics,
state_impl_generics,
superstate_ident,
superstate_derives,
superstate_generics,
superstate_impl_generics,
on_transition,
on_dispatch,
visibility,
Expand Down Expand Up @@ -719,6 +733,8 @@ fn create_analyze_state_machine() -> analyze::StateMachine {
fn create_lower_state_machine() -> StateMachine {
let mut superstate_generics = Generics::default();
superstate_generics.params.push(parse_quote!('sub));
let mut superstate_impl_generics = superstate_generics.clone();
superstate_impl_generics.where_clause = parse_quote!(where Self: 'sub);
StateMachine {
initial_state: parse_quote!(State::on()),
shared_storage_type: parse_quote!(Blinky),
Expand All @@ -729,9 +745,11 @@ fn create_lower_state_machine() -> StateMachine {
state_ident: parse_quote!(State),
state_derives: vec![parse_quote!(Copy), parse_quote!(Clone)],
state_generics: Generics::default(),
state_impl_generics: Generics::default(),
superstate_ident: parse_quote!(Superstate),
superstate_derives: vec![parse_quote!(Copy), parse_quote!(Clone)],
superstate_generics,
superstate_impl_generics,
on_transition: None,
on_dispatch: None,
visibility: parse_quote!(pub),
Expand Down
Loading

0 comments on commit cce10a4

Please sign in to comment.