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. This trait
allows a developer to provide a strict state machine definition, e.g.
specify its:
Note that on the implementation level such abstraction allows build any type of state machines:
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).
The DSL is parsed by the state_machine
macro. Here is a little example.
```rust use rust_fsm::*;
state_machine! { CircuitBreaker(Closed)
Closed(Unsuccessful) => Open [SetupTimer],
Open(TimerTriggered) => HalfOpen,
HalfOpen => {
Successful => Closed,
Unsuccessful => Open [SetupTimer],
}
} ```
This code sample:
CircuitBreaker
;Closed
;Successful
input when in the HalfOpen
state, the machine must move to the Closed
state;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: StateMachine<CircuitBreaker> = StateMachine::new();
// Consume the `Successful` input. No state transition is performed.
let _ = machine.consume(&CircuitBreakerInput::Successful);
// Consume the `Unsuccesful` input. The machine is moved to the `Open`
// state. The output is `SetupTimer`.
let output = machine.consume(&CircuitBreakerInput::Unsuccesful).unwrap();
// 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:
CircuitBreaker
that implements the StateMachineImpl
trait.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.
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.