Finite state machines in rust; bendns fork to add types.
-rw-r--r--README.md89
-rw-r--r--rust_fsm/src/lib.rs89
-rw-r--r--rust_fsm_dsl/src/lib.rs3
3 files changed, 175 insertions, 6 deletions
diff --git a/README.md b/README.md
index d9f20bf..e07a85c 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,18 @@
[![Build Status][build-badge]][build-link]
The `rust-fsm` crate provides a simple and universal framework for building
-state machines in Rust with minimum effort. The essential part of this crate
-is the `StateMachine` trait. This trait allows a developer to provide a
-strict state machine definition, e.g. specify its:
+state machines in Rust with minimum effort. This is achieved by two
+components:
+
+* The `rust-fsm` crate, that provides data types for building state machines
+ and convenience wrappers for these types.
+* The `rust-fsm-dsl` crate, that contains the `state_machine` macro that
+ parses a simple DSL and generates all boilerplate code for the described
+ state machine.
+
+The essential part of this crate is the [`StateMachine`] trait. This trait
+allows a developer to provide a strict state machine definition, e.g.
+specify its:
* An input alphabet - a set of entities that the state machine takes as
inputs and performs state transitions based on them.
@@ -29,6 +38,80 @@ of state machines:
## Use
+Initially this library was designed to build an easy to use DSL for defining
+state machines on top of it. Using the DSL will require to connect an
+additional crate `rust-fsm-dsl` (this is due to limitation of the procedural
+macros system).
+
+### Using the DSL for defining state machines
+
+The DSL is parsed by the `state_machine` macro. Here is a little example.
+
+```rust
+#[macro_use]
+extern crate rust_fsm_dsl;
+
+use use rust_fsm::*;
+
+state_machine! {
+ CircuitBreaker(Closed)
+
+ Closed(Unsuccessful) => Open [SetupTimer],
+ Open(TimerTriggered) => HalfOpen,
+ HalfOpen(Successful) => Closed,
+ HalfOpen(Unsuccessful) => Open [SetupTimer],
+}
+```
+
+This code sample:
+
+* Defines a state machine called `CircuitBreaker`;
+* 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`
+ state;
+* Defines outputs. For example: on receiving `Unsuccessful` in the
+ `Closed` state, the machine must output `SetupTimer`.
+
+This state machine can be used as follows:
+
+```rust
+// Initialize the state machine. The state is `Closed` now.
+let mut machine: StateMachineWrapper<CircuitBreaker> = StateMachineWrapper::new();
+// Consume the `Successful` input. No state transition is performed. Output
+// is `None`.
+machine.consume_anyway(&CircuitBreakerInput::Successful);
+// Consume the `Unsuccesful` input. The machine is moved to the `Open`
+// state. The output is `SetupTimer`.
+let output = machine.consume_anyway(&CircuitBreakerInput::Unsuccesful);
+// Check the output
+if output == Some(CircuitBreakerOutput::SetupTimer) {
+ // Set up the timer...
+}
+// Check the state
+if machine.state() == &CircuitBreakerState::Open {
+ // Do something...
+}
+```
+
+As you can see, the following entities are generated:
+
+* An empty structure `CircuitBreaker` that implements the `StateMachine`
+ trait.
+* Enums `CircuitBreakerState`, `CircuitBreakerInput` and
+ `CircuitBreakerOutput` that represent the state, the input alphabet and
+ the output alphabet respectively.
+
+Note that if there is no outputs in the specification, the output alphabet
+is set to `()`. The set of states and the input alphabet must be non-empty
+sets.
+
+### Without DSL
+
+The `state_machine` macro has limited capabilities (for example, a state
+cannot carry any additional data), so in certain complex cases a user might
+want to write a more complex state machine by hand.
+
All you need to do to build a state machine is to implement the
`StateMachine` trait and use it in conjuctions with some of the provided
wrappers (for now there is only `StateMachineWrapper`).
diff --git a/rust_fsm/src/lib.rs b/rust_fsm/src/lib.rs
index d660dec..e98e145 100644
--- a/rust_fsm/src/lib.rs
+++ b/rust_fsm/src/lib.rs
@@ -1,9 +1,18 @@
//! A framework for building finite state machines in Rust
//!
//! The `rust-fsm` crate provides a simple and universal framework for building
-//! state machines in Rust with minimum effort. The essential part of this crate
-//! is the [`StateMachine`] trait. This trait allows a developer to provide a
-//! strict state machine definition, e.g. specify its:
+//! state machines in Rust with minimum effort. This is achieved by two
+//! components:
+//!
+//! * The `rust-fsm` crate, that provides data types for building state machines
+//! and convenience wrappers for these types.
+//! * The `rust-fsm-dsl` crate, that contains the `state_machine` macro that
+//! parses a simple DSL and generates all boilerplate code for the described
+//! state machine.
+//!
+//! The essential part of this crate is the [`StateMachine`] trait. This trait
+//! allows a developer to provide a strict state machine definition, e.g.
+//! specify its:
//!
//! * An input alphabet - a set of entities that the state machine takes as
//! inputs and performs state transitions based on them.
@@ -27,6 +36,80 @@
//!
//! # Use
//!
+//! Initially this library was designed to build an easy to use DSL for defining
+//! state machines on top of it. Using the DSL will require to connect an
+//! additional crate `rust-fsm-dsl` (this is due to limitation of the procedural
+//! macros system).
+//!
+//! ## Using the DSL for defining state machines
+//!
+//! The DSL is parsed by the `state_machine` macro. Here is a little example.
+//!
+//! ```rust
+//! #[macro_use]
+//! extern crate rust_fsm_dsl;
+//!
+//! use use rust_fsm::*;
+//!
+//! state_machine! {
+//! CircuitBreaker(Closed)
+//!
+//! Closed(Unsuccessful) => Open [SetupTimer],
+//! Open(TimerTriggered) => HalfOpen,
+//! HalfOpen(Successful) => Closed,
+//! HalfOpen(Unsuccessful) => Open [SetupTimer],
+//! }
+//! ```
+//!
+//! This code sample:
+//!
+//! * Defines a state machine called `CircuitBreaker`;
+//! * 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`
+//! state;
+//! * Defines outputs. For example: on receiving `Unsuccessful` in the
+//! `Closed` state, the machine must output `SetupTimer`.
+//!
+//! This state machine can be used as follows:
+//!
+//! ```rust
+//! // Initialize the state machine. The state is `Closed` now.
+//! let mut machine: StateMachineWrapper<CircuitBreaker> = StateMachineWrapper::new();
+//! // Consume the `Successful` input. No state transition is performed. Output
+//! // is `None`.
+//! machine.consume_anyway(&CircuitBreakerInput::Successful);
+//! // Consume the `Unsuccesful` input. The machine is moved to the `Open`
+//! // state. The output is `SetupTimer`.
+//! let output = machine.consume_anyway(&CircuitBreakerInput::Unsuccesful);
+//! // Check the output
+//! if output == Some(CircuitBreakerOutput::SetupTimer) {
+//! // Set up the timer...
+//! }
+//! // Check the state
+//! if machine.state() == &CircuitBreakerState::Open {
+//! // Do something...
+//! }
+//! ```
+//!
+//! As you can see, the following entities are generated:
+//!
+//! * An empty structure `CircuitBreaker` that implements the `StateMachine`
+//! trait.
+//! * Enums `CircuitBreakerState`, `CircuitBreakerInput` and
+//! `CircuitBreakerOutput` that represent the state, the input alphabet and
+//! the output alphabet respectively.
+//!
+//! Note that if there is no outputs in the specification, the output alphabet
+//! is set to `()`. The set of states and the input alphabet must be non-empty
+//! sets.
+//!
+//! ## Without DSL
+//!
+//! The `state_machine` macro has limited capabilities (for example, a state
+//! cannot carry any additional data), so in certain complex cases a user might
+//! want to write a more complex state machine by hand.
+//!
//! All you need to do to build a state machine is to implement the
//! `StateMachine` trait and use it in conjuctions with some of the provided
//! wrappers (for now there is only `StateMachineWrapper`).
diff --git a/rust_fsm_dsl/src/lib.rs b/rust_fsm_dsl/src/lib.rs
index a16fb1b..b33edc2 100644
--- a/rust_fsm_dsl/src/lib.rs
+++ b/rust_fsm_dsl/src/lib.rs
@@ -1,3 +1,6 @@
+//! DSL implementation for defining finite state machines for `rust-fsm`. See
+//! more in the `rust-fsm` crate documentation.
+
#![recursion_limit = "128"]
extern crate proc_macro;