Skip to content

Commit

Permalink
Merge pull request #1 from yuk1ty/feature/add-state-machine-trait
Browse files Browse the repository at this point in the history
Add `StateMachineBuilder` trait
  • Loading branch information
yuk1ty authored Feb 3, 2021
2 parents 51752e0 + 30df05c commit 28d4f31
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 33 deletions.
12 changes: 8 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
//! from given string ("next") and then, it produces outputs.
//!
//! ```rust
//! use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
//! use statemachine_rs::machine::{
//! builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
//! };
//!
//! let sm = StateMachineBuilder::start()
//! let sm = BasicStateMachineBuilder::start()
//! .initial_state(1)
//! .transition(|state, input| match (state, input) {
//! (1, "next") => 2,
Expand All @@ -35,7 +37,9 @@
//! The following example describes if you press the button, the state turns to be `On`. Otherwise, `Off`.
//!
//! ```rust
//! use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
//! use statemachine_rs::machine::{
//! builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
//! };
//!
//! #[derive(Clone, Debug, PartialEq)]
//! enum ButtonState {
Expand All @@ -47,7 +51,7 @@
//! Press,
//! }
//!
//! let sm = StateMachineBuilder::start()
//! let sm = BasicStateMachineBuilder::start()
//! .initial_state(ButtonState::Off)
//! .transition(|state, input| match (state, input) {
//! (ButtonState::On, Input::Press) => ButtonState::Off,
Expand Down
58 changes: 39 additions & 19 deletions src/machine/builder.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
use std::{cell::RefCell, marker::PhantomData};

use super::{error::StateMachineError, BasicStateMachine, StateMachine, StateWrapper};
use super::{error::StateMachineError, BasicStateMachine, StateWrapper};

pub trait StateMachineBuilder<State, Input, Transition>
where
Transition: Fn(&State, Input) -> State,
State: Clone,
{
type Output;

/// Starts the builder.
fn start() -> Self;

/// Sets particular initial state to the state machine.
fn initial_state(self, state: State) -> Self;

/// Sets particular state to the current state.
fn current_state(self, state: State) -> Self;

/// Sets particular transition algorithm to the state machine.
fn transition(self, next: Transition) -> Self;

/// To finish the builder. If it fails, returns [`crate::machine::error::StateMachineError`].
fn build(self) -> Result<Self::Output, Box<dyn std::error::Error>>;
}

/// This builder enables us to assemble StateMachine
/// (like [`crate::machine::BasicStateMachine`]) more easily.
pub struct StateMachineBuilder<State, Input, Transition>
pub struct BasicStateMachineBuilder<State, Input, Transition>
where
Transition: Fn(&State, Input) -> State,
State: Clone,
Expand All @@ -16,35 +39,33 @@ where
}

impl<State, Input, Transition> StateMachineBuilder<State, Input, Transition>
for BasicStateMachineBuilder<State, Input, Transition>
where
Transition: Fn(&State, Input) -> State,
State: Clone,
{
/// Starts the builder.
pub fn start() -> Self {
type Output = BasicStateMachine<State, Input, Transition>;

fn start() -> Self {
Self::default()
}

/// Sets particular initial state to the state machine.
pub fn initial_state(mut self, state: State) -> Self {
fn initial_state(mut self, state: State) -> Self {
self.initial_state = Some(state);
self
}

/// Sets particular state to the current state.
pub fn current_state(mut self, state: State) -> Self {
fn current_state(mut self, state: State) -> Self {
self.current_state = Some(state);
self
}

/// Sets particular transition algorithm to the state machine.
pub fn transition(mut self, next: Transition) -> Self {
fn transition(mut self, next: Transition) -> Self {
self.transition = Some(next);
self
}

/// To finish the builder. If it fails, returns [`crate::machine::error::StateMachineError`].
pub fn build(self) -> Result<impl StateMachine<State, Input>, Box<dyn std::error::Error>> {
fn build(self) -> Result<Self::Output, Box<dyn std::error::Error>> {
match (self.initial_state, self.transition) {
(Some(initial_state), Some(transition)) => Ok(BasicStateMachine {
initial_state: initial_state.clone(),
Expand All @@ -70,13 +91,13 @@ where
}
}

impl<State, Input, Transition> Default for StateMachineBuilder<State, Input, Transition>
impl<State, Input, Transition> Default for BasicStateMachineBuilder<State, Input, Transition>
where
Transition: Fn(&State, Input) -> State,
State: Clone,
{
fn default() -> Self {
StateMachineBuilder {
BasicStateMachineBuilder {
initial_state: None,
current_state: None,
transition: None,
Expand All @@ -87,10 +108,9 @@ where

#[cfg(test)]
mod test {
use super::{BasicStateMachineBuilder, StateMachineBuilder};
use crate::machine::StateMachine;

use super::StateMachineBuilder;

#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum Stations {
Expand All @@ -112,7 +132,7 @@ mod test {
#[test]
fn test_build() {
// sets only initial state
let sm = StateMachineBuilder::start()
let sm = BasicStateMachineBuilder::start()
.initial_state(Stations::Shibuya)
.transition(|station, train| match (station, train) {
(Stations::Shibuya, Train::Local) => Stations::IkejiriOhashi,
Expand All @@ -130,7 +150,7 @@ mod test {
assert_eq!(Stations::Shibuya, sm.current_state());

// sets current state after initializing initial state
let sm = StateMachineBuilder::start()
let sm = BasicStateMachineBuilder::start()
.initial_state(Stations::Shibuya)
.current_state(Stations::Sangendyaya)
.transition(|station, train| match (station, train) {
Expand All @@ -151,7 +171,7 @@ mod test {

#[test]
fn test_fail_initial_state() {
let sm = StateMachineBuilder::start()
let sm = BasicStateMachineBuilder::start()
.transition(|station, train| match (station, train) {
(Stations::Shibuya, Train::Local) => Stations::IkejiriOhashi,
(Stations::Shibuya, Train::Express) => Stations::Sangendyaya,
Expand Down
30 changes: 20 additions & 10 deletions src/machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ pub trait StateMachine<State, Input> {
///
/// # Example
/// ```
/// use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
/// use statemachine_rs::machine::{
/// builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
/// };
///
/// #[derive(Clone, Debug, PartialEq)]
/// enum ButtonState {
Expand All @@ -26,7 +28,7 @@ pub trait StateMachine<State, Input> {
/// Press,
/// }
///
/// let sm = StateMachineBuilder::start()
/// let sm = BasicStateMachineBuilder::start()
/// .initial_state(ButtonState::Off)
/// .transition(|state, input| match (state, input) {
/// (ButtonState::On, Input::Press) => ButtonState::Off,
Expand All @@ -43,7 +45,9 @@ pub trait StateMachine<State, Input> {
///
/// # Example
/// ```
/// use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
/// use statemachine_rs::machine::{
/// builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
/// };
///
/// #[derive(Clone, Debug, PartialEq)]
/// enum ButtonState {
Expand All @@ -55,7 +59,7 @@ pub trait StateMachine<State, Input> {
/// Press,
/// }
///
/// let sm = StateMachineBuilder::start()
/// let sm = BasicStateMachineBuilder::start()
/// .initial_state(ButtonState::Off)
/// .transition(|state, input| match (state, input) {
/// (ButtonState::On, Input::Press) => ButtonState::Off,
Expand All @@ -73,7 +77,9 @@ pub trait StateMachine<State, Input> {
///
/// # Example
/// ```
/// use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
/// use statemachine_rs::machine::{
/// builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
/// };
///
/// #[derive(Clone, Debug, PartialEq)]
/// enum ButtonState {
Expand All @@ -85,7 +91,7 @@ pub trait StateMachine<State, Input> {
/// Press,
/// }
///
/// let sm = StateMachineBuilder::start()
/// let sm = BasicStateMachineBuilder::start()
/// .initial_state(ButtonState::Off)
/// .transition(|state, input| match (state, input) {
/// (ButtonState::On, Input::Press) => ButtonState::Off,
Expand All @@ -103,7 +109,9 @@ pub trait StateMachine<State, Input> {
///
/// # Example
/// ```
/// use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
/// use statemachine_rs::machine::{
/// builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
/// };
///
/// #[derive(Clone, Debug, PartialEq)]
/// enum ButtonState {
Expand All @@ -115,7 +123,7 @@ pub trait StateMachine<State, Input> {
/// Press,
/// }
///
/// let sm = StateMachineBuilder::start()
/// let sm = BasicStateMachineBuilder::start()
/// .initial_state(ButtonState::Off)
/// .transition(|state, input| match (state, input) {
/// (ButtonState::On, Input::Press) => ButtonState::Off,
Expand All @@ -133,7 +141,9 @@ pub trait StateMachine<State, Input> {
///
/// # Example
/// ```
/// use statemachine_rs::machine::{builder::StateMachineBuilder, StateMachine};
/// use statemachine_rs::machine::{
/// builder::BasicStateMachineBuilder, builder::StateMachineBuilder, StateMachine,
/// };
///
/// #[derive(Clone, Debug, PartialEq)]
/// enum ButtonState {
Expand All @@ -146,7 +156,7 @@ pub trait StateMachine<State, Input> {
/// Press,
/// }
///
/// let sm = StateMachineBuilder::start()
/// let sm = BasicStateMachineBuilder::start()
/// .initial_state(ButtonState::Off)
/// .transition(|state, input| match (state, input) {
/// (ButtonState::On, Input::Press) => ButtonState::Off,
Expand Down

0 comments on commit 28d4f31

Please sign in to comment.