Finite state machines in rust; bendns fork to add types.
-rw-r--r--rust-fsm/Cargo.toml1
-rw-r--r--rust-fsm/src/lib.rs47
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
+ }
+}