Finite state machines in rust; bendns fork to add types.
fix docs
bendn 4 months ago
parent 46b0bee · commit b8d464a
-rw-r--r--README.md100
-rw-r--r--rust-fsm-dsl/src/lib.rs2
-rw-r--r--rust-fsm/src/lib.rs67
-rw-r--r--rust-fsm/tests/circuit_breaker_dsl.rs2
-rw-r--r--rust-fsm/tests/circuit_breaker_dsl_custom_types.rs6
-rw-r--r--rust-fsm/tests/simple.rs2
6 files changed, 70 insertions, 109 deletions
diff --git a/README.md b/README.md
index 92bf644..e453c7b 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,21 @@
-# A framework and a DSL for building finite state machines in Rust
-
[![Documentation][docs-badge]][docs-link]
[![Latest Version][crate-badge]][crate-link]
-[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/ybabichenko)
-
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
-[`StateMachineImpl`](trait.StateMachineImpl.html) trait. This trait allows a
+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.
+- An input alphabet - a set of entities that the state machine takes as
+ inputs and performs state transitions based on them.
- Possible states - a set of states this machine could be in.
-- An output alphabet - a set of entities that the state machine may output as
- results of its work.
-- A transition function - a function that changes the state of the state machine
- based on its current state and the provided input.
+- An output alphabet - a set of entities that the state machine may output
+ as results of its work.
+- A transition function - a function that changes the state of the state
+ machine based on its current state and the provided input.
- An output function - a function that outputs something from the output
alphabet based on the current state and the provided inputs.
-- The initial state of the machine.
-
-Note that on the implementation level such abstraction allows build any type of
-state machines:
-
-- A classical state machine by providing only an input alphabet, a set of states
- and a transition function.
-- A Mealy machine by providing all entities listed above.
-- A Moore machine by providing an output function that do not depend on the
- provided inputs.
## Feature flags
@@ -46,8 +31,8 @@ state machines:
## Usage in `no_std` environments
-This library has the feature named `std` which is enabled by default. You may
-want to import this library as
+This library has the feature named `std` which is enabled by default. You
+may want to import this library as
`rust-fsm = { version = "0.8", default-features = false, features = ["dsl"] }`
to use it in a `no_std` environment. This only affects error types (the `Error`
trait is only available in `std`).
@@ -58,9 +43,9 @@ also enabled by default.
## 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).
+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
@@ -73,7 +58,7 @@ state_machine! {
#[derive(Debug)]
#[repr(C)]
/// A Circuit Breaker state machine.
- circuit_breaker(Closed)
+ CircuitBreaker => Result => Action
Closed => Unsuccessful => Open [SetupTimer],
Open => TimerTriggered => HalfOpen,
@@ -86,46 +71,40 @@ state_machine! {
This code sample:
-- Defines a state machine called `circuit_breaker`;
+- Defines a state machine called `CircuitBreaker`;
- Derives the `Debug` trait for it. All attributes you use here (like
- `#[repr(C)]`) will be applied to all types generated by this macro. If you
- want to apply attributes or a docstring to the `mod` generated by this macro,
- just put it before the macro invocation.
-- 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`.
+ `#[repr(C)]`) will be applied to all types generated by this macro.
+- 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,ignore
// Initialize the state machine. The state is `Closed` now.
-let mut machine = circuit_breaker::StateMachine::new();
+let mut machine = CircuitBreaker::Closed;
// Consume the `Successful` input. No state transition is performed.
-let _ = machine.consume(circuit_breaker::Input::Successful);
+let _ = machine.consume(Result::Successful);
// Consume the `Unsuccesful` input. The machine is moved to the `Open`
// state. The output is `SetupTimer`.
-let output = machine.consume(circuit_breaker::Input::Unsuccessful).unwrap();
+let output = machine.consume(Result::Unsuccessful).unwrap();
// Check the output
-if let Some(circuit_breaker::Output::SetupTimer) = output {
+if let Some(Action::SetupTimer) = output {
// Set up the timer...
}
// Check the state
-if let circuit_breaker::State::Open = machine.state() {
+if let CircuitBreaker::Open = machine {
// Do something...
}
```
The following entities are generated:
-- An empty structure `circuit_breaker::Impl` that implements the
- `StateMachineImpl` trait.
-- Enums `circuit_breaker::State`, `circuit_breaker::Input` and
- `circuit_breaker::Output` that represent the state, the input alphabet and the
+- Enums `CircuitBreaker`, `Result` and
+ `Action` that represent the state, the input alphabet and the
output alphabet respectively.
-- Type alias `circuit_breaker::StateMachine` that expands to
- `StateMachine<circuit_breaker::Impl>`.
Note that if there is no outputs in the specification, the output alphabet is an
empty enum and due to technical limitations of many Rust attributes, no
@@ -141,13 +120,13 @@ You can specify visibility like this:
use rust_fsm::*;
state_machine! {
- pub CircuitBreaker(Closed)
+ pub CircuitBreaker => 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]
}
}
```
@@ -180,11 +159,10 @@ pub enum Output {
}
state_machine! {
- #[state_machine(input(crate::Input), state(crate::State), output(crate::Output))]
- circuit_breaker(Closed)
+ crate::State => crate::Input => crate::Output
- Closed(Unsuccessful) => Open [SetupTimer],
- Open(TimerTriggered) => HalfOpen,
+ Closed => Unsuccessful => Open [SetupTimer],
+ Open => TimerTriggered => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer]
@@ -209,16 +187,16 @@ cargo doc -p doc-example --open
### 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.
+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
`StateMachineImpl` trait and use it in conjuctions with some of the provided
wrappers (for now there is only `StateMachine`).
-You can see an example of the Circuit Breaker state machine in the [project
-repository][repo].
+You can see an example of the Circuit Breaker state machine in the
+[project repository][repo].
[repo]: https://github.com/eugene-babichenko/rust-fsm
[docs-badge]: https://docs.rs/rust-fsm/badge.svg
diff --git a/rust-fsm-dsl/src/lib.rs b/rust-fsm-dsl/src/lib.rs
index ed1d729..40a487d 100644
--- a/rust-fsm-dsl/src/lib.rs
+++ b/rust-fsm-dsl/src/lib.rs
@@ -225,7 +225,7 @@ pub fn state_machine(tokens: TokenStream) -> TokenStream {
#state_impl
#output_impl
- impl ::rust_fsm::StateMachineImpl for #state_name {
+ impl ::rust_fsm::StateMachine for #state_name {
type Input = #input_name;
type Output = #output_name;
diff --git a/rust-fsm/src/lib.rs b/rust-fsm/src/lib.rs
index ed3d338..1ab4c74 100644
--- a/rust-fsm/src/lib.rs
+++ b/rust-fsm/src/lib.rs
@@ -5,8 +5,7 @@
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
-[`StateMachineImpl`](trait.StateMachineImpl.html) trait. This trait allows a
+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
@@ -18,16 +17,6 @@ developer to provide a strict state machine definition, e.g. specify its:
machine based on its current state and the provided input.
* An output function - a function that outputs something from the output
alphabet based on the current state and the provided inputs.
-* The initial state of the machine.
-
-Note that on the implementation level such abstraction allows build any type
-of state machines:
-
-* A classical state machine by providing only an input alphabet, a set of
- states and a transition function.
-* A Mealy machine by providing all entities listed above.
-* A Moore machine by providing an output function that do not depend on the
- provided inputs.
## Feature flags
@@ -70,10 +59,10 @@ state_machine! {
#[derive(Debug)]
#[repr(C)]
/// A Circuit Breaker state machine.
- circuit_breaker(Closed)
+ CircuitBreaker => Result => Action
- Closed(Unsuccessful) => Open [SetupTimer],
- Open(TimerTriggered) => HalfOpen,
+ Closed => Unsuccessful => Open [SetupTimer],
+ Open => TimerTriggered => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer]
@@ -83,12 +72,9 @@ state_machine! {
This code sample:
-* Defines a state machine called `circuit_breaker`;
+* Defines a state machine called `CircuitBreaker`;
* Derives the `Debug` trait for it. All attributes you use here (like
- `#[repr(C)]`) will be applied to all types generated by this macro. If you
- want to apply attributes or a docstring to the `mod` generated by this macro,
- just put it before the macro invocation.
-* Sets the initial state of this state machine to `Closed`;
+ `#[repr(C)]`) will be applied to all types generated by this macro.
* Defines state transitions. For example: on receiving the `Successful`
input when in the `HalfOpen` state, the machine must move to the `Closed`
state;
@@ -99,38 +85,33 @@ This state machine can be used as follows:
```rust,ignore
// Initialize the state machine. The state is `Closed` now.
-let mut machine = circuit_breaker::StateMachine::new();
+let mut machine = CircuitBreaker::Closed;
// Consume the `Successful` input. No state transition is performed.
-let _ = machine.consume(circuit_breaker::Input::Successful);
+let _ = machine.consume(Result::Successful);
// Consume the `Unsuccesful` input. The machine is moved to the `Open`
// state. The output is `SetupTimer`.
-let output = machine.consume(circuit_breaker::Input::Unsuccessful).unwrap();
+let output = machine.consume(Result::Unsuccessful).unwrap();
// Check the output
-if let Some(circuit_breaker::Output::SetupTimer) = output {
+if let Some(Action::SetupTimer) = output {
// Set up the timer...
}
// Check the state
-if let circuit_breaker::State::Open = machine.state() {
+if let CircuitBreaker::Open = machine {
// Do something...
}
```
The following entities are generated:
-* An empty structure `circuit_breaker::Impl` that implements the
- `StateMachineImpl` trait.
-* Enums `circuit_breaker::State`, `circuit_breaker::Input` and
- `circuit_breaker::Output` that represent the state, the input alphabet and the
+* Enums `CircuitBreaker`, `Result` and
+ `Action` that represent the state, the input alphabet and the
output alphabet respectively.
-* Type alias `circuit_breaker::StateMachine` that expands to
- `StateMachine<circuit_breaker::Impl>`.
Note that if there is no outputs in the specification, the output alphabet is an
empty enum and due to technical limitations of many Rust attributes, no
attributes (e.g. `derive`, `repr`) are applied to it.
-Within the `state_machine` macro you must define at least one state
-transition.
+Within the `state_machine` macro you must define at least one state transition.
#### Visibility
@@ -140,7 +121,7 @@ You can specify visibility like this:
use rust_fsm::*;
state_machine! {
- pub CircuitBreaker: Closed => Result => Action
+ pub CircuitBreaker => Result => Action
Closed => Unsuccessful => Open [SetupTimer],
Open => TimerTriggered => HalfOpen,
@@ -179,11 +160,10 @@ pub enum Output {
}
state_machine! {
- #[state_machine(input(crate::Input), state(crate::State), output(crate::Output))]
- circuit_breaker(Closed)
+ crate::State => crate::Input => crate::Output
- Closed(Unsuccessful) => Open [SetupTimer],
- Open(TimerTriggered) => HalfOpen,
+ Closed => Unsuccessful => Open [SetupTimer],
+ Open => TimerTriggered => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer]
@@ -247,21 +227,24 @@ 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: Sized {
+pub trait StateMachine: Sized {
/// The input alphabet.
type Input;
/// The output alphabet.
type Output;
/// 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. Also gives you the output, if any.
+ /// state and the provided input. Outputs [`Err`] (allowing recovery of the state and input)
+ /// when there is no transition for a given combination of the input and the state.
+ /// Also gives you the output, if any.
+ ///
+ /// This function is discouraged from panicking.
fn transition(
self,
input: Self::Input,
) -> Result<(Self, Option<Self::Output>), TransitionImpossibleError<Self, Self::Input>>;
/// 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.
+ /// provided input is not allowed, returns an error containing the input.
///
/// Aborts if `transition` panics.
fn consume(
diff --git a/rust-fsm/tests/circuit_breaker_dsl.rs b/rust-fsm/tests/circuit_breaker_dsl.rs
index 0e58486..8d5e0f3 100644
--- a/rust-fsm/tests/circuit_breaker_dsl.rs
+++ b/rust-fsm/tests/circuit_breaker_dsl.rs
@@ -9,7 +9,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
- #[derive(Clone, Copy, Debug)]
+ #[derive(Debug)]
pub CircuitBreaker => Result => Action
Closed => Unsuccessful => Open [SetupTimer],
diff --git a/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs b/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs
index bdf6829..389f913 100644
--- a/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs
+++ b/rust-fsm/tests/circuit_breaker_dsl_custom_types.rs
@@ -5,21 +5,21 @@ use rust_fsm::*;
use std::sync::{Arc, Mutex};
use std::time::Duration;
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
pub enum Input {
Successful,
Unsuccessful,
TimerTriggered,
}
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
pub enum State {
Closed,
HalfOpen,
Open,
}
-#[derive(Clone, Copy, Debug)]
+#[derive(Debug)]
pub enum Output {
SetupTimer,
}
diff --git a/rust-fsm/tests/simple.rs b/rust-fsm/tests/simple.rs
index 6f4a7b5..3810dcf 100644
--- a/rust-fsm/tests/simple.rs
+++ b/rust-fsm/tests/simple.rs
@@ -1,7 +1,7 @@
use rust_fsm::*;
state_machine! {
- #[derive(Debug, Clone, Copy)]
+ #[derive(Debug)]
#[repr(C)]
Door => Action => __