Finite state machines in rust; bendns fork to add types.
more major changes
bendn 5 months ago
parent 92f0370 · commit a99d458
-rw-r--r--doc-example/src/lib.rs2
-rw-r--r--rust-fsm-dsl/src/lib.rs169
-rw-r--r--rust-fsm-dsl/src/parser.rs80
-rw-r--r--rust-fsm/src/lib.rs101
-rw-r--r--rust-fsm/tests/circuit_breaker.rs106
-rw-r--r--rust-fsm/tests/circuit_breaker_dsl.rs27
-rw-r--r--rust-fsm/tests/circuit_breaker_dsl_custom_types.rs13
-rw-r--r--rust-fsm/tests/simple.rs21
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:?}");
}