Crates.io | finite-state-machine |
lib.rs | finite-state-machine |
version | 0.2.0 |
source | src |
created_at | 2023-04-12 13:40:23.630097 |
updated_at | 2023-04-13 22:48:08.29023 |
description | A type and trait based finite state machine macro |
homepage | |
repository | https://github.com/koriwi/finite-state-machine |
max_upload_size | |
id | 836938 |
size | 20,443 |
Create finite state machines in rust with macros. The macro generates a struct, event enums and traits and also a decider trait. The decider trait is used to decide which state to transition to. Each state has a function which is called automatically when the state is entered. An invalid state and an end state are automatically added. You also need to implement a function for each transition. The macro automatically generates traits for everything you need to implement so the validity is checked on compile time.
use finite_state_machine::state_machine;
// Debug is only needed if the verbose feature is enabled
#[derive(Debug, Default)]
struct Data {
...
}
state_machine!(
// The name of the state machine and the type of the data, you can also use livetimes here
CircuitBreaker(Data);
// the first state will automatically become the start state, no matter the name
Closed {
Ok => Closed, // on Ok event go to Closed state
AmperageTooHigh => Open // on AmperageTooHigh event go to open state
},
Open {
AttemptReset => HalfOpen,
Wait => Open
},
HalfOpen {
Success => Closed,
AmperageTooHigh => Open,
MaxAttemps => End
}
);
use circuit_breaker::*;
// now you need to implement the decider trait which emits events which decide which state to transition to
impl Deciders for CircuitBreaker {
fn closed(&self) -> circuit_breaker::ClosedEvents {
if self.data.current_amperage > self.data.max_amperage {
circuit_breaker::ClosedEvents::AmperageTooHigh
} else {
circuit_breaker::ClosedEvents::Ok
}
}
...
}
// now we need to implement the transition trait for each state
impl ClosedTransitions for CircuitBreaker {
fn amperage_too_high(&mut self) -> Result<(), &'static str> {
self.data.tripped_at = Some(SystemTime::now());
Ok(())
}
fn ok(&mut self) -> Result<(), &'static str> {
self.data.current_amperage += 1;
std::thread::sleep(Duration::from_millis(500));
Ok(())
}
fn illegal(&mut self) {}
}
For more details, check the examples folder.
CircuitBreaker
-> circuit_breaker
.impl Deciders for CircuitBreaker
. The decider functions only get a non mutable reference &self
and must return an enum with a variant for each transition. This enum is generated by the macro. In our case circuit_breaker::ClosedEvents
, etc.
There is also an Illegal
variant which is used when you encounter a state of the struct which is illegal/impossible. This will transition to the Invalid
state. In an isolated state machine this should never happen. But if you use the state machine in a larger system, this can happen.<StateName>Transitions
trait will be generated which you need to implement for your struct. In our case impl ClosedTransitions for CircuitBreaker
. These functions get a mutable reference to self &mut self
and must return a Result<(), &'static str>
. The &'static str
is used to return an error message. If you return an Err
the state machine will transition to the invalid state which is automatically added. When entering this state the machine stops and returns to you the error message.You can enable the feature verbose
to get more information about the state machine. This will print the current state, the transition and the current data.