Finite state machines in rust; bendns fork to add types.
more major changes
| -rw-r--r-- | doc-example/src/lib.rs | 2 | ||||
| -rw-r--r-- | rust-fsm-dsl/src/lib.rs | 169 | ||||
| -rw-r--r-- | rust-fsm-dsl/src/parser.rs | 80 | ||||
| -rw-r--r-- | rust-fsm/src/lib.rs | 101 | ||||
| -rw-r--r-- | rust-fsm/tests/circuit_breaker.rs | 106 | ||||
| -rw-r--r-- | rust-fsm/tests/circuit_breaker_dsl.rs | 27 | ||||
| -rw-r--r-- | rust-fsm/tests/circuit_breaker_dsl_custom_types.rs | 13 | ||||
| -rw-r--r-- | rust-fsm/tests/simple.rs | 21 |
8 files changed, 174 insertions, 345 deletions
diff --git a/doc-example/src/lib.rs b/doc-example/src/lib.rs index 43691ee..397b4a8 100644 --- a/doc-example/src/lib.rs +++ b/doc-example/src/lib.rs @@ -4,7 +4,7 @@ state_machine! { /// A dummy implementation of the Circuit Breaker pattern to demonstrate /// capabilities of its library DSL for defining finite state machines. /// https://martinfowler.com/bliki/CircuitBreaker.html - pub circuit_breaker(Closed) + pub CircuitBreaker: Closed => Result => Action Closed => Unsuccessful => Open [SetupTimer], Open => TimerTriggered => HalfOpen, 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() diff --git a/rust-fsm-dsl/src/parser.rs b/rust-fsm-dsl/src/parser.rs index b0fe02e..f1a9b67 100644 --- a/rust-fsm-dsl/src/parser.rs +++ b/rust-fsm-dsl/src/parser.rs @@ -1,4 +1,5 @@ use super::variant::Variant; +use proc_macro2::TokenStream; use syn::{ parse::{Error, Parse, ParseStream, Result}, token::Bracket, @@ -116,26 +117,41 @@ pub struct StateMachineDef { pub doc: Vec<Attribute>, /// The visibility modifier (applies to all generated items) pub visibility: Visibility, - pub name: Ident, + pub state_name: ImplementationRequired, + pub input_name: ImplementationRequired, + pub output_name: ImplementationRequired, pub initial_state: Variant, pub transitions: Vec<TransitionDef>, pub attributes: Vec<Attribute>, - pub input_type: Option<Path>, - pub state_type: Option<Path>, - pub output_type: Option<Path>, +} + +pub enum ImplementationRequired { + Yes(Ident), + No(Path), +} + +impl ImplementationRequired { + pub fn tokenize(&self, f: impl Fn(&Ident) -> TokenStream) -> TokenStream { + match self { + ImplementationRequired::Yes(ident) => f(ident), + ImplementationRequired::No(_) => TokenStream::default(), + } + } + pub fn path(self) -> Path { + match self { + ImplementationRequired::Yes(ident) => ident.into(), + ImplementationRequired::No(path) => path, + } + } } impl Parse for StateMachineDef { fn parse(input: ParseStream) -> Result<Self> { - let mut state_machine_attributes = Vec::new(); let mut doc = Vec::new(); let attributes = Attribute::parse_outer(input)? .into_iter() .filter_map(|attribute| { - if attribute.path().is_ident("state_machine") { - state_machine_attributes.push(attribute); - None - } else if attribute.path().is_ident("doc") { + if attribute.path().is_ident("doc") { doc.push(attribute); None } else { @@ -144,34 +160,21 @@ impl Parse for StateMachineDef { }) .collect(); - let mut input_type = None; - let mut state_type = None; - let mut output_type = None; - - for attribute in state_machine_attributes { - attribute.parse_nested_meta(|meta| { - let content; - parenthesized!(content in meta.input); - let p: Path = content.parse()?; - - if meta.path.is_ident("input") { - input_type = Some(p); - } else if meta.path.is_ident("state") { - state_type = Some(p); - } else if meta.path.is_ident("output") { - output_type = Some(p); - } - - Ok(()) - })?; - } - let visibility = input.parse()?; - let name = input.parse()?; + let i = || { + input + .parse::<Ident>() + .map(ImplementationRequired::Yes) + .or_else(|_| input.parse::<Path>().map(ImplementationRequired::No)) + }; + let state_name = i()?; + input.parse::<Token![:]>()?; + let initial_state = input.parse()?; - let initial_state_content; - parenthesized!(initial_state_content in input); - let initial_state = initial_state_content.parse()?; + input.parse::<Token![=>]>()?; + let input_name = i()?; + input.parse::<Token![=>]>()?; + let output_name = i()?; let transitions = input .parse_terminated(TransitionDef::parse, Token![,])? @@ -181,13 +184,12 @@ impl Parse for StateMachineDef { Ok(Self { doc, visibility, - name, + state_name, + input_name, + output_name, initial_state, transitions, attributes, - input_type, - state_type, - output_type, }) } } diff --git a/rust-fsm/src/lib.rs b/rust-fsm/src/lib.rs index 5a44016..4bb192b 100644 --- a/rust-fsm/src/lib.rs +++ b/rust-fsm/src/lib.rs @@ -140,13 +140,13 @@ You can specify visibility like this: use rust_fsm::*; state_machine! { - pub CircuitBreaker(Closed) + pub CircuitBreaker: Closed => Result => Action - Closed(Unsuccessful) => Open [SetupTimer], - Open(TimerTriggered) => HalfOpen, + Closed => Unsuccessful => Open [SetupTimer], + Open => TimerTriggered => HalfOpen, HalfOpen => { Successful => Closed, - Unsuccessful => Open [SetupTimer], + Unsuccessful => Open [SetupTimer] } } ``` @@ -229,7 +229,7 @@ You can see an example of the Circuit Breaker state machine in the #![cfg_attr(not(feature = "std"), no_std)] -use core::{fmt, ops::Deref}; +use core::fmt; #[cfg(feature = "std")] use std::error::Error; @@ -243,100 +243,45 @@ pub use aquamarine::aquamarine; /// machine/transducer. This is just a formal definition that may be /// inconvenient to be used in practical programming, but it is used throughout /// this library for more practical things. -pub trait StateMachineImpl { +pub trait StateMachineImpl: Sized { /// The input alphabet. type Input; - /// The set of possible states. - type State; /// The output alphabet. type Output; /// The initial state of the machine. // allow since there is usually no interior mutability because states are enums #[allow(clippy::declare_interior_mutable_const)] - const INITIAL_STATE: Self::State; + const INITIAL_STATE: Self; /// The transition fuction that outputs a new state based on the current /// state and the provided input. Outputs `None` when there is no transition /// for a given combination of the input and the state. - fn transition(state: Self::State, input: Self::Input) -> Option<Self::State>; + fn transition(self, input: Self::Input) -> Option<Self>; /// The output function that outputs some value from the output alphabet /// based on the current state and the given input. Outputs `None` when /// there is no output for a given combination of the input and the state. - fn output(state: Self::State, input: Self::Input) -> Option<Self::Output>; -} - -/// A convenience wrapper around the `StateMachine` trait that encapsulates the -/// state and transition and output function calls. -#[derive(Debug, Clone)] -pub struct StateMachine<T: StateMachineImpl> { - state: T::State, -} - -#[derive(Debug, Clone)] -/// An error type that represents that the state transition is impossible given -/// the current combination of state and input. -pub struct TransitionImpossibleError; - -impl<T> StateMachine<T> -where - T: StateMachineImpl, -{ - /// Create a new instance of this wrapper which encapsulates the initial - /// state. - pub fn new() -> Self { - Self::from_state(T::INITIAL_STATE) - } - - /// Create a new instance of this wrapper which encapsulates the given - /// state. - pub fn from_state(state: T::State) -> Self { - Self { state } - } - - pub fn transition(self, input: T::Input) -> Result<T::State, TransitionImpossibleError> { - T::transition(self.state, input).ok_or(TransitionImpossibleError) - } - pub fn output(self, input: T::Input) -> Result<T::Output, TransitionImpossibleError> { - T::output(self.state, input).ok_or(TransitionImpossibleError) - } - - /// Consumes the provided input, gives an output and performs a state - /// transition. If a state transition with the current state and the - /// provided input is not allowed, returns an error. - pub fn consume( + fn output(self, input: Self::Input) -> Option<Self::Output>; + fn consume( &mut self, - input: T::Input, - ) -> Result<Option<T::Output>, TransitionImpossibleError> + input: Self::Input, + ) -> Result<Option<Self::Output>, TransitionImpossibleError> where - T::Input: Clone, - T::State: Clone, + Self::Input: Clone, + Self: Clone, { - T::transition(self.state.clone(), input.clone()) + self.clone() + .transition(input.clone()) .ok_or(TransitionImpossibleError) - .map(|state| T::output(std::mem::replace(&mut self.state, state), input)) + .map(|state| std::mem::replace(self, state).output(input)) } - - /// Returns the current state. - pub fn state(&self) -> &T::State { - &self.state + fn new() -> Self { + Self::INITIAL_STATE } } -impl<T: StateMachineImpl> Deref for StateMachine<T> { - type Target = T::State; - - fn deref(&self) -> &Self::Target { - &self.state - } -} - -impl<T> Default for StateMachine<T> -where - T: StateMachineImpl, -{ - fn default() -> Self { - Self::new() - } -} +#[derive(Debug, Clone)] +/// An error type that represents that the state transition is impossible given +/// the current combination of state and input. +pub struct TransitionImpossibleError; impl fmt::Display for TransitionImpossibleError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/rust-fsm/tests/circuit_breaker.rs b/rust-fsm/tests/circuit_breaker.rs deleted file mode 100644 index d8e300d..0000000 --- a/rust-fsm/tests/circuit_breaker.rs +++ /dev/null @@ -1,106 +0,0 @@ -/// A dummy implementation of the Circuit Breaker pattern to demonstrate -/// capabilities of this library. -/// https://martinfowler.com/bliki/CircuitBreaker.html -use rust_fsm::*; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - -#[derive(Debug, Clone, Copy)] -enum CircuitBreakerInput { - Successful, - Unsuccessful, - TimerTriggered, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -enum CircuitBreakerState { - Closed, - Open, - HalfOpen, -} - -#[derive(Debug, PartialEq)] -struct CircuitBreakerOutputSetTimer; - -#[derive(Debug)] -struct CircuitBreakerMachine; - -impl StateMachineImpl for CircuitBreakerMachine { - type Input = CircuitBreakerInput; - type State = CircuitBreakerState; - type Output = CircuitBreakerOutputSetTimer; - const INITIAL_STATE: Self::State = CircuitBreakerState::Closed; - - fn transition(state: Self::State, input: Self::Input) -> Option<Self::State> { - match (state, input) { - (CircuitBreakerState::Closed, CircuitBreakerInput::Unsuccessful) => { - Some(CircuitBreakerState::Open) - } - (CircuitBreakerState::Open, CircuitBreakerInput::TimerTriggered) => { - Some(CircuitBreakerState::HalfOpen) - } - (CircuitBreakerState::HalfOpen, CircuitBreakerInput::Successful) => { - Some(CircuitBreakerState::Closed) - } - (CircuitBreakerState::HalfOpen, CircuitBreakerInput::Unsuccessful) => { - Some(CircuitBreakerState::Open) - } - _ => None, - } - } - - fn output(state: Self::State, input: Self::Input) -> Option<Self::Output> { - match (state, input) { - (CircuitBreakerState::Closed, CircuitBreakerInput::Unsuccessful) => { - Some(CircuitBreakerOutputSetTimer) - } - (CircuitBreakerState::HalfOpen, CircuitBreakerInput::Unsuccessful) => { - Some(CircuitBreakerOutputSetTimer) - } - _ => None, - } - } -} - -#[test] -fn circuit_breaker() { - let machine: StateMachine<CircuitBreakerMachine> = StateMachine::new(); - - // Unsuccessful request - let machine = Arc::new(Mutex::new(machine)); - { - let mut lock = machine.lock().unwrap(); - let res = lock.consume(CircuitBreakerInput::Unsuccessful).unwrap(); - assert_eq!(res, Some(CircuitBreakerOutputSetTimer)); - assert_eq!(lock.state(), &CircuitBreakerState::Open); - } - - // Set up a timer - let machine_wait = machine.clone(); - std::thread::spawn(move || { - std::thread::sleep(Duration::new(5, 0)); - let mut lock = machine_wait.lock().unwrap(); - let res = lock.consume(CircuitBreakerInput::TimerTriggered).unwrap(); - assert_eq!(res, None); - assert_eq!(lock.state(), &CircuitBreakerState::HalfOpen); - }); - - // Try to pass a request when the circuit breaker is still open - let machine_try = machine.clone(); - std::thread::spawn(move || { - std::thread::sleep(Duration::new(1, 0)); - let mut lock = machine_try.lock().unwrap(); - let res = lock.consume(CircuitBreakerInput::Successful); - assert!(matches!(res, Err(TransitionImpossibleError))); - assert_eq!(lock.state(), &CircuitBreakerState::Open); - }); - - // Test if the circit breaker was actually closed - std::thread::sleep(Duration::new(7, 0)); - { - let mut lock = machine.lock().unwrap(); - let res = lock.consume(CircuitBreakerInput::Successful).unwrap(); - assert_eq!(res, None); - assert_eq!(lock.state(), &CircuitBreakerState::Closed); - } -} diff --git a/rust-fsm/tests/circuit_breaker_dsl.rs b/rust-fsm/tests/circuit_breaker_dsl.rs index c7315c1..407ac0d 100644 --- a/rust-fsm/tests/circuit_breaker_dsl.rs +++ b/rust-fsm/tests/circuit_breaker_dsl.rs @@ -6,8 +6,11 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; state_machine! { + /// A dummy implementation of the Circuit Breaker pattern to demonstrate + /// capabilities of its library DSL for defining finite state machines. + /// https://martinfowler.com/bliki/CircuitBreaker.html #[derive(Clone, Copy)] - circuit_breaker(Closed) + pub CircuitBreaker: Closed => Result => Action Closed => Unsuccessful => Open [SetupTimer], Open => TimerTriggered => HalfOpen, @@ -19,15 +22,15 @@ state_machine! { #[test] fn circit_breaker_dsl() { - let machine = circuit_breaker::StateMachine::new(); + let machine = CircuitBreaker::new(); // Unsuccessful request let machine = Arc::new(Mutex::new(machine)); { let mut lock = machine.lock().unwrap(); - let res = lock.consume(circuit_breaker::Input::Unsuccessful).unwrap(); - assert!(matches!(res, Some(circuit_breaker::Output::SetupTimer))); - assert!(matches!(lock.state(), &circuit_breaker::State::Open)); + let res = lock.consume(Result::Unsuccessful).unwrap(); + assert!(matches!(res, Some(Action::SetupTimer))); + assert!(matches!(*lock, CircuitBreaker::Open)); } // Set up a timer @@ -35,11 +38,9 @@ fn circit_breaker_dsl() { std::thread::spawn(move || { std::thread::sleep(Duration::new(5, 0)); let mut lock = machine_wait.lock().unwrap(); - let res = lock - .consume(circuit_breaker::Input::TimerTriggered) - .unwrap(); + let res = lock.consume(Result::TimerTriggered).unwrap(); assert!(matches!(res, None)); - assert!(matches!(lock.state(), &circuit_breaker::State::HalfOpen)); + assert!(matches!(*lock, CircuitBreaker::HalfOpen)); }); // Try to pass a request when the circuit breaker is still open @@ -47,17 +48,17 @@ fn circit_breaker_dsl() { std::thread::spawn(move || { std::thread::sleep(Duration::new(1, 0)); let mut lock = machine_try.lock().unwrap(); - let res = lock.consume(circuit_breaker::Input::Successful); + let res = lock.consume(Result::Successful); assert!(matches!(res, Err(TransitionImpossibleError))); - assert!(matches!(lock.state(), &circuit_breaker::State::Open)); + assert!(matches!(*lock, CircuitBreaker::Open)); }); // Test if the circit breaker was actually closed std::thread::sleep(Duration::new(7, 0)); { let mut lock = machine.lock().unwrap(); - let res = lock.consume(circuit_breaker::Input::Successful).unwrap(); + let res = lock.consume(Result::Successful).unwrap(); assert!(matches!(res, None)); - assert!(matches!(lock.state(), &circuit_breaker::State::Closed)); + assert!(matches!(*lock, CircuitBreaker::Closed)); } } diff --git a/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs b/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs index c17617b..0d1b95e 100644 --- a/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs +++ b/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs @@ -25,8 +25,7 @@ pub enum Output { } state_machine! { - #[state_machine(input(crate::Input), state(crate::State), output(crate::Output))] - circuit_breaker(Closed) + crate::State: Closed => crate::Input => crate::Output Closed => Unsuccessful => Open [SetupTimer], Open => TimerTriggered => HalfOpen, @@ -38,7 +37,7 @@ state_machine! { #[test] fn circit_breaker_dsl() { - let machine = circuit_breaker::StateMachine::new(); + let machine = State::new(); // Unsuccessful request let machine = Arc::new(Mutex::new(machine)); @@ -46,7 +45,7 @@ fn circit_breaker_dsl() { let mut lock = machine.lock().unwrap(); let res = lock.consume(Input::Unsuccessful).unwrap(); assert!(matches!(res, Some(Output::SetupTimer))); - assert!(matches!(lock.state(), &State::Open)); + assert!(matches!(*lock, State::Open)); } // Set up a timer @@ -56,7 +55,7 @@ fn circit_breaker_dsl() { let mut lock = machine_wait.lock().unwrap(); let res = lock.consume(Input::TimerTriggered).unwrap(); assert!(matches!(res, None)); - assert!(matches!(lock.state(), &State::HalfOpen)); + assert!(matches!(*lock, State::HalfOpen)); }); // Try to pass a request when the circuit breaker is still open @@ -66,7 +65,7 @@ fn circit_breaker_dsl() { let mut lock = machine_try.lock().unwrap(); let res = lock.consume(Input::Successful); assert!(matches!(res, Err(TransitionImpossibleError))); - assert!(matches!(lock.state(), &State::Open)); + assert!(matches!(*lock, State::Open)); }); // Test if the circit breaker was actually closed @@ -75,6 +74,6 @@ fn circit_breaker_dsl() { let mut lock = machine.lock().unwrap(); let res = lock.consume(Input::Successful).unwrap(); assert!(matches!(res, None)); - assert!(matches!(lock.state(), &State::Closed)); + assert!(matches!(*lock, State::Closed)); } } diff --git a/rust-fsm/tests/simple.rs b/rust-fsm/tests/simple.rs index d7bdf7c..4ff4389 100644 --- a/rust-fsm/tests/simple.rs +++ b/rust-fsm/tests/simple.rs @@ -3,26 +3,21 @@ use rust_fsm::*; state_machine! { #[derive(Debug, Clone, Copy)] #[repr(C)] - door(Open) + Door: Open => Action => __ Open => Key => Closed, Closed => Key => Open, Open => Break => Broken, Closed => Break => Broken, - Open => Thing(u32 => 5) => Fine, - // Open(u32) => Key => Open [output], - // Open(u32) => { - // Key => Open, - // } } #[test] fn simple() { - let mut machine = door::StateMachine::new(); - machine.consume(door::Input::Key).unwrap(); - println!("{:?}", machine.state()); - machine.consume(door::Input::Key).unwrap(); - println!("{:?}", machine.state()); - machine.consume(door::Input::Break).unwrap(); - println!("{:?}", machine.state()); + let mut machine = Door::Open; + machine.consume(Action::Key).unwrap(); + println!("{machine:?}"); + machine.consume(Action::Key).unwrap(); + println!("{machine:?}"); + machine.consume(Action::Break).unwrap(); + println!("{machine:?}"); } |