From a94baea32e75c80a5a955940bf3d0eb109c40a27 Mon Sep 17 00:00:00 2001 From: Ben Wolsieffer Date: Fri, 18 Nov 2022 01:00:23 -0500 Subject: [PATCH 1/2] Implement conversion from non-det transitions to enumeration Implement From conversions from non-deterministic transition enums to the state enum, if enabled. --- typestate-proc-macro/src/lib.rs | 48 +++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) 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; From 4f3ad8eb254a2eedcb54f590b767b03a5cd31df7 Mon Sep 17 00:00:00 2001 From: Ben Wolsieffer Date: Fri, 9 Dec 2022 22:55:11 -0500 Subject: [PATCH 2/2] Add test for conversion from non-det transition to state enum --- .../pass/non-deterministic_enumerate_into.rs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/pass/non-deterministic_enumerate_into.rs 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); +}