1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! MicroState
//!
//! A minimal Finite State Machine.
//!
//! Example:
//!
//! ```
//! #[macro_use] extern crate microstate;
//! microstate! {
//!     Simple { A }
//!     states { A, B }
//!
//!     next {
//!         A => B
//!         B => A
//!     }
//! }
//!
//! # fn main() {
//! use Simple::State::*;
//!
//! let mut machine = Simple::new();
//! assert_eq!(A, machine.state());
//! assert_eq!(Some(B), machine.next());
//! assert_eq!(Some(A), machine.next());
//! # }
//! ```

/// Create a new state machine
///
/// It takes a name, the initial value, all possible states
/// and the transitions.
///
/// See the main documentation for a proper example.
#[macro_export]
macro_rules! microstate (
  (
      $machine:ident { $initial:ident }
      states { $($states:ident),* }

      $($meth:ident {
          $($from:ident => $to:ident)*
      })*
  ) => (
      #[allow(non_snake_case)]
      pub mod $machine {
          #[derive(Clone,PartialEq,Eq,Debug)]
          pub enum State {
              __InvalidState__, // Just be able to match _ further down
              $($states),*
          }
          #[derive(PartialEq,Eq,Debug)]
          pub struct Machine {
              state: State,
          }

          pub fn new() -> Machine {
              Machine::new()
          }

          impl Machine {
              pub fn new() -> Machine {
                  Machine {
                      state: State::$initial
                  }
              }

              pub fn state(&self) -> State {
                  self.state.clone()
              }

              $(pub fn $meth(&mut self) -> Option<State> {
                  match self.state {
                      $( State::$from => { self.state = State::$to; Some(State::$to) } ),*
                          _ => None
                  }
              })*
          }
      }
));

#[cfg(test)]
mod tests {
    microstate!{
        Micro { New }
        states { New, Confirmed, Ignored }

        confirm {
            New => Confirmed
        }

        ignore {
            New => Ignored
        }

        reset {
            Confirmed => New
            Ignored   => New
        }
    }
    use self::Micro::State::*;

    #[test]
    fn test_transition_works() {
        let mut machine = Micro::new();

        assert_eq!(New, machine.state());

        assert_eq!(Some(Confirmed), machine.confirm());
        assert_eq!(Confirmed, machine.state());

        assert_eq!(None, machine.ignore());
        assert_eq!(Confirmed, machine.state());

        assert_eq!(Some(New), machine.reset());
        assert_eq!(New, machine.state());

        assert_eq!(Some(Ignored), machine.ignore());
        assert_eq!(Ignored, machine.state());
    }
}