2 releases

0.1.1 Feb 11, 2024
0.1.0 Feb 11, 2024

#1161 in Rust patterns

MIT license

12KB
252 lines

Turbostate

I just needed a state machine, so I wrote this.

Features

feature description
async Async support, only tokio
from_residual Short-circuit error conversion from Result::Err to Flow::Failure

By example

use turbostate::{Engine, Flow, Machine};

#[derive(Debug, Default, PartialEq, Eq)]
enum State {
	#[default]
	Idle,
	Dialing,
	Ringing,
	Connected,
	Disconnected,
}

enum Event {
	Dial,
	IncomingCall,
	Answer,
	Reject,
	HangUp,
	Reset,
}

#[derive(Debug, Default)]
struct Shared {
	calls_made: u32,
}

#[derive(Debug)]
enum Error {
	InvalidTransition,
}

#[derive(Debug, Default)]
struct CallEngine;

impl Engine for CallEngine {
	type Error = Error;
	type Event = Event;
	type Shared = Shared;
	type State = State;

	fn next(
		state: &Self::State,
		event: Self::Event,
		shared: &mut Self::Shared,
	) -> Flow<Self::State, Self::Error, Self::Event> {
		use Event::*;
		use Flow::*;
		use State::*;

		let flow = match (state, event) {
			(Idle, Dial) => Transition(Dialing),
			(Dialing, Reject) => Transition(Idle),
			(Dialing, Answer) => Transition(Connected),
			(Connected, HangUp) => Transition(Disconnected),
			(Disconnected, Reset) => Transition(Idle),
			(Idle, IncomingCall) => Transition(Ringing),
			(Ringing, Reject) => Transition(Idle),
			(Ringing, Answer) => Transition(Connected),
			// With `from_residual` feature enabled you can do `Err(InvalidTransition)?`
			_ => Failure(Error::InvalidTransition),
		};

		if let Transition(Connected) = flow {
			shared.calls_made += 1;
		}

		flow
	}
}

type CallMachine = Machine<CallEngine>;

fn main() {
	let machine = CallMachine::default();

	machine.fire(Event::Dial).unwrap();
	machine.fire(Event::Answer).unwrap();
	machine.fire(Event::HangUp).unwrap();

	machine.fire(Event::Reset).unwrap();
	machine.fire(Event::IncomingCall).unwrap();
	machine.fire(Event::Answer).unwrap();
	machine.fire(Event::HangUp).unwrap();

	let destructed = machine.destruct();
	assert_eq!(destructed.state, State::Disconnected);
	assert_eq!(destructed.shared.calls_made, 2);
}

Dependencies

~0–5.5MB
~22K SLoC