# mode
[](https://travis-ci.com/andrewtc/mode)
A simple and effective behavioral state machine library, written in in idiomatic, 100% safe, stable Rust code.
This library provides three main types, `Automaton`, `Mode`, and `Transition`, that facilitate the creation of
behavioral state machines. An `Automaton` can be used to quickly create a state machine over a set of `Mode`s that
implement some `Base` type. Each struct that implements `Mode` represents a distinct state in the state machine, and
the `Automaton` allows function calls to be dispatched to the current `Mode` by providing access to it as a `Base`
reference. A flexible `Transition` system provides a way for the current `Mode` to swap in a new state when it is
ready. The `Transition` system is designed such that the current `Mode` can move data from itself directly into the
`Mode` being created, which can help prevent spikes in memory usage while switching from one state to the next.
## Releases
See the full list of releases on [GitHub](https://github.com/andrewtc/mode/releases).
## Documentation
Please see [docs.rs](https://docs.rs/mode) for detailed documentation.
## Example
```rust
use mode::*;
// This trait will be used as the Base type for the Automaton, defining a common interface
// for all states.
trait Activity {
fn update(&mut self);
}
// Each state in the state machine implements both Activity (the Base type) and Mode.
struct Working {
pub hours_worked : u32,
}
impl Activity for Working {
fn update(&mut self) {
println!("Work, work, work...");
self.hours_worked += 1;
}
}
impl<'a> Mode<'a> for Working {
type Base = Activity + 'a;
fn as_base(&self) -> &Self::Base { self }
fn as_base_mut(&mut self) -> &mut Self::Base { self }
// This function allows the current Mode to swap to another Mode, when ready.
fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> {
if self.hours_worked == 4 || self.hours_worked >= 8 {
// To swap to another Mode, a Transition function is returned, which will consume
// the current Mode and return a new Mode to be swapped in as active.
Some(Box::new(|previous : Self| {
println!("Time for {}!", if previous.hours_worked == 4 { "lunch" } else { "dinner" });
Eating { hours_worked: previous.hours_worked, calories_consumed: 0 }
}))
}
else { None } // None means don't transition.
}
}
struct Eating {
pub hours_worked : u32,
pub calories_consumed : u32,
}
impl Activity for Eating {
fn update(&mut self) {
println!("Yum!");
self.calories_consumed += 100;
}
}
impl<'a> Mode<'a> for Eating {
type Base = Activity + 'a;
fn as_base(&self) -> &Self::Base { self }
fn as_base_mut(&mut self) -> &mut Self::Base { self }
fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> {
if self.calories_consumed >= 500 {
if self.hours_worked >= 8 {
println!("Time for bed!");
Some(Box::new(|_ : Self| { Sleeping { hours_rested: 0 } }))
}
else {
println!("Time to go back to work!");
Some(Box::new(|previous : Self| {
Working { hours_worked: previous.hours_worked }
}))
}
}
else { None }
}
}
struct Sleeping {
pub hours_rested : u32,
}
impl Activity for Sleeping {
fn update(&mut self) {
println!("ZzZzZzZz...");
self.hours_rested += 1;
}
}
impl<'a> Mode<'a> for Sleeping {
type Base = Activity + 'a;
fn as_base(&self) -> &Self::Base { self }
fn as_base_mut(&mut self) -> &mut Self::Base { self }
fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> {
if self.hours_rested >= 8 {
println!("Time for breakfast!");
Some(Box::new(|_| { Eating { hours_worked: 0, calories_consumed: 0 } }))
}
else { None }
}
}
fn main() {
let mut person = Automaton::with_initial_mode(Working { hours_worked: 0 });
for _age in 18..100 {
// Update the current Mode for the Automaton.
// NOTE: We can call update() on the inner Mode through the Automaton reference,
// due to Deref coercion.
person.update();
// Allow the Automaton to switch Modes.
person.perform_transitions();
}
}
```
# License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/andrewtc/mode/blob/master/LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](https://github.com/andrewtc/mode/blob/master/LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
## Contributing
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as
defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
If you find bugs, please feel free to open an issue on [GitHub](https://github.com/andrewtc/mode/issues)! Otherwise, if
you would like to propose changes to this library, feel free to send me a pull request and I will handle them as best I
can. Thanks!