Skip to content
This repository was archived by the owner on Jan 11, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions tests/pass/non-deterministic_enumerate_into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use typestate_proc_macro::typestate;
use m::*;

#[typestate(enumerate="ES")]
mod m {
#[automaton]
pub struct S;

#[state]
pub struct A;

#[state]
pub struct B;

#[state]
pub struct Error;

pub trait A {
fn start() -> A;
fn next(self) -> Fallible;
}

pub trait B {
fn end(self);
}

pub trait Error {
fn consume(self);
}

pub enum Fallible {
B,
Error,
}
}

impl AState for S<A> {
fn start() -> Self {
Self { state: A }
}

fn next(self) -> Fallible {
Fallible::B(S { state: B })
}
}

fn main() {
let s = S::<A>::start();
let f = s.next();
ES::from(f);
}
48 changes: 46 additions & 2 deletions typestate-proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,12 @@ pub fn typestate(args: TokenStream, input: TokenStream) -> TokenStream {
let mut enumerate_tokens = if !args.enumerate.is_empty() {
let mut res: Vec<Item> = vec![];
let enum_ident = &format_ident!("{}", &args.enumerate);
res.expand_enumerate(&automata_ident, enum_ident, &states);
res.expand_enumerate(
&automata_ident,
enum_ident,
&states,
&state_machine_info.non_det_transitions,
);
res
} else {
vec![]
Expand Down Expand Up @@ -252,7 +257,13 @@ fn export_diagram_files(state_machine_info: &StateMachineInfo) {
}

trait ExpandEnumerate {
fn expand_enumerate(&mut self, automata: &ItemStruct, automata_enum: &Ident, states: &[&Ident]);
fn expand_enumerate(
&mut self,
automata: &ItemStruct,
automata_enum: &Ident,
states: &[&Ident],
non_det_transitions: &HashMap<Ident, ItemEnum>,
);
/// Expand the [`ToString`] implentation for enumeration.
/// Only available with `std` and when `enumerate` is used.
fn expand_to_string(&mut self, automata_enum: &Ident, states: &[&Ident]);
Expand All @@ -262,6 +273,14 @@ trait ExpandEnumerate {
/// Expand the [`From`] implementation to convert from states to enumeration and back.
/// Only available when `enumerate` is used.
fn expand_from(&mut self, automata: &ItemStruct, automata_enum: &Ident, states: &[&Ident]);
/// Expand the [`From`] implementations to convert from non-deterministic transitions to enumeration.
/// Only available when `enumerate` is used.
fn expand_non_det_from(
&mut self,
automata: &ItemStruct,
automata_enum: &Ident,
non_det_transitions: &HashMap<Ident, ItemEnum>,
);
}

impl ExpandEnumerate for Vec<Item> {
Expand All @@ -270,12 +289,14 @@ impl ExpandEnumerate for Vec<Item> {
automata: &ItemStruct,
automata_enum: &Ident,
states: &[&Ident],
non_det_transitions: &HashMap<Ident, ItemEnum>,
) {
// expand the enumeration
self.expand_enum(automata, automata_enum, states);

// expand conversion traits: `From`
self.expand_from(automata, automata_enum, states);
self.expand_non_det_from(automata, automata_enum, non_det_transitions);

// if std is present, generate `to_string` implementations
#[cfg(feature = "std")]
Expand Down Expand Up @@ -309,6 +330,29 @@ impl ExpandEnumerate for Vec<Item> {
self.extend(from_tokens);
}

fn expand_non_det_from(
&mut self,
automata: &ItemStruct,
automata_enum: &Ident,
non_det_transitions: &HashMap<Ident, ItemEnum>,
) {
let from_tokens = non_det_transitions
.iter()
.map(|(transition_enum, states_variants)| {
let states = states_variants.variants.iter().map(|variant| variant.ident.clone());
::syn::parse_quote! {
impl ::core::convert::From<#transition_enum> for #automata_enum {
fn from(value: #transition_enum) -> Self {
match value {
#(#transition_enum::#states(state) => Self::#states(state)),*
}
}
}
}
});
self.extend(from_tokens);
}

fn expand_enum(&mut self, automata: &ItemStruct, automata_enum: &Ident, states: &[&Ident]) {
let automata_ident = &automata.ident;
let automata_vis = &automata.vis;
Expand Down