diff --git a/tests/pass/non-deterministic_enumerate_into.rs b/tests/pass/non-deterministic_enumerate_into.rs
new file mode 100644
index 0000000..3f686e2
--- /dev/null
+++ b/tests/pass/non-deterministic_enumerate_into.rs
@@ -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 {
+ fn start() -> Self {
+ Self { state: A }
+ }
+
+ fn next(self) -> Fallible {
+ Fallible::B(S { state: B })
+ }
+}
+
+fn main() {
+ let s = S::::start();
+ let f = s.next();
+ ES::from(f);
+}
diff --git a/typestate-proc-macro/src/lib.rs b/typestate-proc-macro/src/lib.rs
index 7d9ff90..4c93f7d 100644
--- a/typestate-proc-macro/src/lib.rs
+++ b/typestate-proc-macro/src/lib.rs
@@ -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- = 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![]
@@ -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,
+ );
/// 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]);
@@ -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,
+ );
}
impl ExpandEnumerate for Vec
- {
@@ -270,12 +289,14 @@ impl ExpandEnumerate for Vec
- {
automata: &ItemStruct,
automata_enum: &Ident,
states: &[&Ident],
+ non_det_transitions: &HashMap,
) {
// 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")]
@@ -309,6 +330,29 @@ impl ExpandEnumerate for Vec
- {
self.extend(from_tokens);
}
+ fn expand_non_det_from(
+ &mut self,
+ automata: &ItemStruct,
+ automata_enum: &Ident,
+ non_det_transitions: &HashMap,
+ ) {
+ 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;