Finite state machines in rust; bendns fork to add types.
| -rw-r--r-- | rust-fsm/Cargo.toml | 1 | ||||
| -rw-r--r-- | rust-fsm/src/lib.rs | 47 |
2 files changed, 40 insertions, 8 deletions
diff --git a/rust-fsm/Cargo.toml b/rust-fsm/Cargo.toml index 84645ed..83eccdc 100644 --- a/rust-fsm/Cargo.toml +++ b/rust-fsm/Cargo.toml @@ -20,6 +20,7 @@ diagram = ["aquamarine", "rust-fsm-dsl/diagram"] [dependencies] aquamarine = { version = "0.6", optional = true } +replace_with = "0.1.8" rust-fsm-dsl = { path = "../rust-fsm-dsl", version = "0.8.0", optional = true } [profile.dev] diff --git a/rust-fsm/src/lib.rs b/rust-fsm/src/lib.rs index 2012892..ed3d338 100644 --- a/rust-fsm/src/lib.rs +++ b/rust-fsm/src/lib.rs @@ -229,10 +229,14 @@ You can see an example of the Circuit Breaker state machine in the #![cfg_attr(not(feature = "std"), no_std)] -use core::fmt::{self, Debug}; +use core::{ + fmt::{self, Debug}, + marker::PhantomData, +}; #[cfg(feature = "std")] use std::error::Error; +use replace_with::replace_with_or_abort_and_return; #[cfg(feature = "dsl")] pub use rust_fsm_dsl::state_machine; @@ -258,16 +262,21 @@ pub trait StateMachineImpl: Sized { /// 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. + /// + /// Aborts if `transition` panics. fn consume( &mut self, input: Self::Input, - ) -> Result<Option<Self::Output>, TransitionImpossibleError<Self, Self::Input>> - where - Self: Clone, - { - self.clone().transition(input).map(|(s, output)| { - *self = s; - output + ) -> Result<Option<Self::Output>, TransitionImpossibleError_<Self, Self::Input>> { + replace_with_or_abort_and_return(self, |x| match x.transition(input) { + Ok((state, ret)) => (Ok(ret), state), + Err(TransitionImpossibleError { state, input }) => ( + Err(TransitionImpossibleError_ { + state: PhantomData, + input, + }), + state, + ), }) } } @@ -279,6 +288,11 @@ pub struct TransitionImpossibleError<S, I> { pub state: S, pub input: I, } +#[derive(Debug, Clone)] +pub struct TransitionImpossibleError_<S, I> { + pub state: PhantomData<S>, + pub input: I, +} impl<S: Debug, I: Debug> fmt::Display for TransitionImpossibleError<S, I> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -290,6 +304,16 @@ impl<S: Debug, I: Debug> fmt::Display for TransitionImpossibleError<S, I> { ) } } +impl<S: Debug, I: Debug> fmt::Display for TransitionImpossibleError_<S, I> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "cannot perform a state transition from the current state (of type {:?}) with the provided input ({:?})", + self.state, + self.input + ) + } +} #[cfg(feature = "std")] impl<S: Debug, I: Debug> Error for TransitionImpossibleError<S, I> { @@ -297,3 +321,10 @@ impl<S: Debug, I: Debug> Error for TransitionImpossibleError<S, I> { None } } + +#[cfg(feature = "std")] +impl<S: Debug, I: Debug> Error for TransitionImpossibleError_<S, I> { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } +} |