Finite state machines in rust; bendns fork to add types.
Diffstat (limited to 'rust-fsm-dsl/src/lib.rs')
-rw-r--r--rust-fsm-dsl/src/lib.rs169
1 files changed, 81 insertions, 88 deletions
diff --git a/rust-fsm-dsl/src/lib.rs b/rust-fsm-dsl/src/lib.rs
index 70cd5a2..621402e 100644
--- a/rust-fsm-dsl/src/lib.rs
+++ b/rust-fsm-dsl/src/lib.rs
@@ -11,6 +11,8 @@ use syn::*;
mod parser;
mod variant;
use variant::Variant;
+
+use crate::parser::StateMachineDef;
/// The full information about a state transition. Used to unify the
/// represantion of the simple and the compact forms.
struct Transition<'a> {
@@ -29,22 +31,28 @@ fn attrs_to_token_stream(attrs: Vec<Attribute>) -> proc_macro2::TokenStream {
/// Produce a state machine definition from the provided `rust-fmt` DSL
/// description.
pub fn state_machine(tokens: TokenStream) -> TokenStream {
- let input = parse_macro_input!(tokens as parser::StateMachineDef);
-
- let doc = attrs_to_token_stream(input.doc);
- let attrs = attrs_to_token_stream(input.attributes);
-
- if input.transitions.is_empty() {
+ let StateMachineDef {
+ doc,
+ visibility,
+ state_name,
+ input_name,
+ output_name,
+ initial_state,
+ transitions,
+ attributes,
+ } = parse_macro_input!(tokens as parser::StateMachineDef);
+
+ let doc = attrs_to_token_stream(doc);
+ let attrs = attrs_to_token_stream(attributes);
+
+ if transitions.is_empty() {
let output = quote! {
compile_error!("rust-fsm: at least one state transition must be provided");
};
return output.into();
}
- let fsm_name = input.name;
- let visibility = input.visibility;
-
- let transitions = input.transitions.iter().flat_map(|def| {
+ let transitions = transitions.iter().flat_map(|def| {
def.transitions.iter().map(move |transition| Transition {
initial_state: &def.initial_state,
input_value: &transition.input_value,
@@ -62,10 +70,10 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
#[cfg(feature = "diagram")]
let mut mermaid_diagram = format!(
"///```mermaid\n///stateDiagram-v2\n/// [*] --> {}\n",
- input.initial_state
+ initial_state
);
- states.insert(&input.initial_state);
+ states.insert(&initial_state);
for transition in transitions {
let Transition {
@@ -81,16 +89,18 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
));
let input_ = input_value.match_on();
+ let final_state_ = final_state.match_on();
transition_cases.push(quote! {
- (Self::State::#initial_state, Self::Input::#input_) => {
- Some(Self::State::#final_state)
+ (Self::#initial_state, Self::Input::#input_) => {
+ Some(Self::#final_state_)
}
});
if let Some(output_value) = output {
+ let output_value_ = output_value.match_on();
output_cases.push(quote! {
- (Self::State::#initial_state, Self::Input::#input_) => {
- Some(Self::Output::#output_value)
+ (Self::#initial_state, Self::Input::#input_) => {
+ Some(Self::Output::#output_value_)
}
});
@@ -114,55 +124,45 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
#[cfg(feature = "diagram")]
let mermaid_diagram: proc_macro2::TokenStream = mermaid_diagram.parse().unwrap();
- let initial_state_name = &input.initial_state;
+ let initial_state_name = &initial_state;
- let (input_type, input_impl) = match input.input_type {
- Some(t) => (quote!(#t), quote!()),
- None => (
- quote!(Input),
- quote! {
- #attrs
- pub enum Input {
- #(#inputs),*
- }
- },
- ),
- };
-
- let (state_type, state_impl) = match input.state_type {
- Some(t) => (quote!(#t), quote!()),
- None => (
- quote!(State),
- quote! {
- #attrs
- pub enum State {
- #(#states),*
- }
- },
- ),
- };
+ let input_impl = input_name.tokenize(|f| {
+ quote! {
+ #attrs
+ #visibility enum #f {
+ #(#inputs),*
+ }
+ }
+ });
+ let input_name = input_name.path();
+ let state_impl = state_name.tokenize(|f| {
+ quote! {
+ #attrs
+ #visibility enum #f {
+ #(#states),*
+ }
+ }
+ });
+ let state_name = state_name.path();
+
+ let output_impl = output_name.tokenize(|output_name| {
+ // Many attrs and derives may work incorrectly (or simply not work) for empty enums, so we just skip them
+ // altogether if the output alphabet is empty.
+ let attrs = if outputs.is_empty() {
+ quote!()
+ } else {
+ attrs.clone()
+ };
- let (output_type, output_impl) = match input.output_type {
- Some(t) => (quote!(#t), quote!()),
- None => {
- // Many attrs and derives may work incorrectly (or simply not work) for empty enums, so we just skip them
- // altogether if the output alphabet is empty.
- let attrs = if outputs.is_empty() {
- quote!()
- } else {
- attrs.clone()
- };
- (
- quote!(Output),
- quote! {
- #attrs
- pub enum Output {
- #(#outputs),*
- }
- },
- )
+ quote! {
+ #attrs
+ #visibility enum #output_name {
+ #(#outputs),*
+ }
}
- };
+ });
+
+ let output_name = output_name.path();
#[cfg(feature = "diagram")]
let diagram = quote! {
@@ -174,39 +174,32 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
let diagram = quote!();
let output = quote! {
+ #input_impl
#doc
#diagram
- #visibility mod #fsm_name {
- #attrs
- pub struct Impl;
-
- pub type StateMachine = ::rust_fsm::StateMachine<Impl>;
-
- #input_impl
- #state_impl
- #output_impl
-
- impl ::rust_fsm::StateMachineImpl for Impl {
- type Input = #input_type;
- type State = #state_type;
- type Output = #output_type;
- const INITIAL_STATE: Self::State = Self::State::#initial_state_name;
-
- fn transition(state: Self::State, input: Self::Input) -> Option<Self::State> {
- match (state, input) {
- #(#transition_cases)*
- _ => None,
- }
+ #state_impl
+ #output_impl
+
+ impl ::rust_fsm::StateMachineImpl for #state_name {
+ type Input = #input_name;
+ type Output = #output_name;
+ const INITIAL_STATE: Self = Self::#initial_state_name;
+
+ fn transition(self, input: Self::Input) -> Option<Self> {
+ match (self, input) {
+ #(#transition_cases)*
+ _ => None,
}
+ }
- fn output(state: Self::State, input: Self::Input) -> Option<Self::Output> {
- match (state, input) {
- #(#output_cases)*
- _ => None,
- }
+ fn output(self, input: Self::Input) -> Option<Self::Output> {
+ match (self, input) {
+ #(#output_cases)*
+ _ => None,
}
}
}
+
};
output.into()