Skip to content

Instantly share code, notes, and snippets.

@cameronp98
Last active August 2, 2017 23:12
Show Gist options
  • Save cameronp98/d707b7d7f5bf8c5c0010459b882d531d to your computer and use it in GitHub Desktop.
Save cameronp98/d707b7d7f5bf8c5c0010459b882d531d to your computer and use it in GitHub Desktop.
Example state machine with 3 states
use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread::{self, JoinHandle};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum Event {
Wait,
Work,
Done,
}
#[derive(Debug)]
struct EventListener {
sender: Sender<Event>,
}
impl EventListener {
fn new(sender: Sender<Event>) -> Self {
EventListener {
sender: sender,
}
}
fn spawn(self, sequence: Vec<Event>) -> JoinHandle<()> {
thread::spawn(move || {
for event in sequence.into_iter() {
self.sender.send(event).unwrap();
}
})
}
}
#[derive(Debug)]
struct Machine<S> {
receiver: Receiver<Event>,
state: S,
}
// State machine with 3 states
// Waiting <--wait/work--> Working --done--> Done
// ^-------------------wait------------------
#[derive(Debug)]
enum MachineError {
EndOfInput,
UnexpectedEvent(Event),
}
type MachineResult<T> = Result<T, MachineError>;
impl<S> Machine<S> {
fn recv(&self) -> MachineResult<Event> {
self.receiver.recv().map_err(|_| MachineError::EndOfInput);
}
fn expect(&self, event: Event) -> MachineResult<()> {
let e = self.recv()?;
if e != event {
return Err(MachineError::UnexpectedEvent(e));
}
Ok(())
}
fn expect_one(&self, valid_events: &[Event]) -> MachineResult<Event> {
let e = self.recv()?;
if !valid_events.contains(&e) {
return Err(MachineError::UnexpectedEvent(e))
}
Ok(e)
}
}
#[derive(Debug)]
struct Waiting;
impl Machine<Waiting> {
fn try_work(self) -> MachineResult<Machine<Working>> {
self.expect(Event::Work)?;
Ok(self.into())
}
}
impl From<Machine<Done>> for Machine<Waiting> {
fn from(machine: Machine<Done>) -> Machine<Waiting> {
println!("DONE to WAIT");
Machine {
receiver: machine.receiver,
state: Waiting,
}
}
}
impl From<Machine<Working>> for Machine<Waiting> {
fn from(machine: Machine<Working>) -> Machine<Waiting> {
println!("WORK to WAIT");
Machine {
receiver: machine.receiver,
state: Waiting,
}
}
}
#[derive(Debug)]
struct Working;
impl Machine<Working> {
fn try_finish(self) -> MachineResult<Machine<Done>> {
// Machine<Working> can go forward to Done or backwards to Waiting
let done = match self.expect_one(&[Event::Done, Event::Wait])? {
Event::Done => {
// Work is done, transition straight to Done
self.into()
},
Event::Wait => {
// Work isn't done yet; wait, then try again
let waiter: Machine<Waiting> = self.into();
waiter.try_work()?.try_finish()?
}
_ => unreachable!(),
};
Ok(done)
}
}
impl From<Machine<Waiting>> for Machine<Working> {
fn from(machine: Machine<Waiting>) -> Machine<Working> {
println!("WAIT to WORK");
Machine {
receiver: machine.receiver,
state: Working,
}
}
}
#[derive(Debug)]
struct Done;
impl Machine<Done> {
// Machine always starts and ends in the Done state
fn new(receiver: Receiver<Event>) -> Self {
Machine {
receiver: receiver,
state: Done,
}
}
fn try_wait(self) -> MachineResult<Machine<Waiting>> {
self.expect(Event::Wait)?;
Ok(self.into())
}
}
impl From<Machine<Working>> for Machine<Done> {
fn from(machine: Machine<Working>) -> Machine<Done> {
println!("WORK to DONE");
Machine {
receiver: machine.receiver,
state: Done,
}
}
}
// Complete sequences until error or exit
fn run_machine(rx: Receiver<Event>) -> MachineResult<()> {
// The sequence always starts/ends as Done
let mut machine = Machine::new(rx);
// Try and complete the state sequence until error or valid exit condition
loop {
// Try to enter the waiting state
match machine.try_wait() {
Ok(waiter) => {
// Ok, now go through the sequence
machine = waiter.try_work()?.try_finish()?;
},
Err(e) => {
// Failed to enter waiting state
match e {
MachineError::UnexpectedEvent(_) => {
// Invalid input for the current state
return Err(e)
},
MachineError::EndOfInput => {
// EOI while Done is the only valid exit condition
return Ok(())
},
}
}
}
}
}
pub fn main() {
// Sender and receiver for events
let (tx, rx) = channel();
// Create some events
EventListener::new(tx).spawn(vec![
Event::Wait,
Event::Work,
Event::Wait,
Event::Work,
Event::Done,
Event::Wait,
Event::Work,
Event::Done,
]);
// Create a machine and receive events
run_machine(rx).unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment