Finite state machines in rust; bendns fork to add types.
-rw-r--r--README.md2
-rw-r--r--examples/circuit_breaker_dsl.rs16
-rw-r--r--examples/simple.rs1
-rw-r--r--rust_fsm_dsl/src/lib.rs13
-rw-r--r--rust_fsm_dsl/src/parser.rs34
-rw-r--r--src/lib.rs2
6 files changed, 57 insertions, 11 deletions
diff --git a/README.md b/README.md
index 02a7bc0..fc1e5e9 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,7 @@ The DSL is parsed by the `state_machine` macro. Here is a little example.
use rust_fsm::*;
state_machine! {
+ derive(Debug)
CircuitBreaker(Closed)
Closed(Unsuccessful) => Open [SetupTimer],
@@ -60,6 +61,7 @@ state_machine! {
This code sample:
* Defines a state machine called `CircuitBreaker`;
+* Derives the `Debug` trait for it (the `derive` section is optional);
* Sets the initial state of this state machine to `Closed`;
* Defines state transitions. For example: on receiving the `Successful`
input when in the `HalfOpen` state, the machine must move to the `Closed`
diff --git a/examples/circuit_breaker_dsl.rs b/examples/circuit_breaker_dsl.rs
index 1410416..7756f5a 100644
--- a/examples/circuit_breaker_dsl.rs
+++ b/examples/circuit_breaker_dsl.rs
@@ -24,8 +24,8 @@ fn main() {
{
let mut lock = machine.lock().unwrap();
let res = lock.consume(&CircuitBreakerInput::Unsuccessful).unwrap();
- assert_eq!(res, Some(CircuitBreakerOutput::SetupTimer));
- assert_eq!(lock.state(), &CircuitBreakerState::Open);
+ assert!(matches!(res, Some(CircuitBreakerOutput::SetupTimer)));
+ assert!(matches!(lock.state(), &CircuitBreakerState::Open));
}
// Set up a timer
@@ -34,8 +34,8 @@ fn main() {
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);
+ assert!(matches!(res, None));
+ assert!(matches!(lock.state(), &CircuitBreakerState::HalfOpen));
});
// Try to pass a request when the circuit breaker is still open
@@ -44,8 +44,8 @@ fn main() {
std::thread::sleep(Duration::new(1, 0));
let mut lock = machine_try.lock().unwrap();
let res = lock.consume(&CircuitBreakerInput::Successful);
- assert_eq!(res, Err(()));
- assert_eq!(lock.state(), &CircuitBreakerState::Open);
+ assert!(matches!(res, Err(())));
+ assert!(matches!(lock.state(), &CircuitBreakerState::Open));
});
// Test if the circit breaker was actually closed
@@ -53,7 +53,7 @@ fn main() {
{
let mut lock = machine.lock().unwrap();
let res = lock.consume(&CircuitBreakerInput::Successful).unwrap();
- assert_eq!(res, None);
- assert_eq!(lock.state(), &CircuitBreakerState::Closed);
+ assert!(matches!(res, None));
+ assert!(matches!(lock.state(), &CircuitBreakerState::Closed));
}
}
diff --git a/examples/simple.rs b/examples/simple.rs
index 81f8b98..3b4048f 100644
--- a/examples/simple.rs
+++ b/examples/simple.rs
@@ -1,6 +1,7 @@
use rust_fsm::*;
state_machine! {
+ derive(Debug)
Door(Open)
Open(Key) => Closed,
diff --git a/rust_fsm_dsl/src/lib.rs b/rust_fsm_dsl/src/lib.rs
index efa2b05..04e9594 100644
--- a/rust_fsm_dsl/src/lib.rs
+++ b/rust_fsm_dsl/src/lib.rs
@@ -24,6 +24,12 @@ struct Transition<'a> {
pub fn state_machine(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as parser::StateMachineDef);
+ let derives = if let Some(derives) = input.derives {
+ quote! { #[derive(#(#derives,)*)] }
+ } else {
+ quote! {}
+ };
+
if input.transitions.is_empty() {
let output = quote! {
compile_error!("rust-fsm: at least one state transition must be provided");
@@ -82,7 +88,7 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
let (outputs_repr, outputs_type, output_impl) = if !outputs.is_empty() {
let outputs_type_name = Ident::new(&format!("{}Output", struct_name), struct_name.span());
let outputs_repr = quote! {
- #[derive(Debug, PartialEq)]
+ #derives
#visibility enum #outputs_type_name {
#(#outputs),*
}
@@ -116,14 +122,15 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
};
let output = quote! {
+ #derives
#visibility struct #struct_name;
- #[derive(Debug, PartialEq)]
+ #derives
#visibility enum #states_enum_name {
#(#states),*
}
- #[derive(Debug, PartialEq)]
+ #derives
#visibility enum #inputs_enum_name {
#(#inputs),*
}
diff --git a/rust_fsm_dsl/src/parser.rs b/rust_fsm_dsl/src/parser.rs
index 0eb3f7c..696fa84 100644
--- a/rust_fsm_dsl/src/parser.rs
+++ b/rust_fsm_dsl/src/parser.rs
@@ -5,6 +5,10 @@ use syn::{
Ident, Token, Visibility,
};
+mod kw {
+ syn::custom_keyword!(derive);
+}
+
/// The output of a state transition
pub struct Output(Option<Ident>);
@@ -101,6 +105,32 @@ impl Parse for TransitionDef {
}
}
+struct Derives {
+ derives: Option<Vec<Ident>>,
+}
+
+impl Parse for Derives {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(kw::derive) {
+ let kw_derive = input.parse::<kw::derive>()?;
+ let entries_content;
+ parenthesized!(entries_content in input);
+ let entries: Vec<_> = entries_content
+ .parse_terminated::<_, Token![,]>(Ident::parse)?
+ .into_iter()
+ .collect();
+ if entries.is_empty() {
+ return Err(Error::new_spanned(kw_derive, "Derive list cannot be empty"));
+ }
+ return Ok(Derives {
+ derives: Some(entries),
+ });
+ }
+ Ok(Derives { derives: None })
+ }
+}
+
/// Parses the whole state machine definition in the following form (example):
///
/// ```rust,ignore
@@ -121,10 +151,13 @@ pub struct StateMachineDef {
pub name: Ident,
pub initial_state: Ident,
pub transitions: Vec<TransitionDef>,
+ pub derives: Option<Vec<Ident>>,
}
impl Parse for StateMachineDef {
fn parse(input: ParseStream) -> Result<Self> {
+ let Derives { derives } = input.parse()?;
+
let visibility = input.parse()?;
let name = input.parse()?;
@@ -142,6 +175,7 @@ impl Parse for StateMachineDef {
name,
initial_state,
transitions,
+ derives,
})
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 2a62405..c5e73d0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,6 +42,7 @@
//! use rust_fsm::*;
//!
//! state_machine! {
+//! derive(Debug)
//! CircuitBreaker(Closed)
//!
//! Closed(Unsuccessful) => Open [SetupTimer],
@@ -56,6 +57,7 @@
//! This code sample:
//!
//! * Defines a state machine called `CircuitBreaker`;
+//! * Derives the `Debug` trait for it (the `derive` section is optional);
//! * Sets the initial state of this state machine to `Closed`;
//! * Defines state transitions. For example: on receiving the `Successful`
//! input when in the `HalfOpen` state, the machine must move to the `Closed`