ready-active-safe 0.1.3

Lifecycle engine for externally driven systems
Documentation
//! Basic lifecycle.
//!
//! A small example that moves through Ready, Active, and Safe.
//! Events drive the transitions. The machine returns decisions as data.

#![allow(clippy::print_stdout, clippy::enum_glob_use)]

use ready_active_safe::prelude::*;

#[derive(Debug, Clone, PartialEq, Eq)]
enum Mode {
    Ready,
    Active,
    Safe,
}

impl core::fmt::Display for Mode {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{self:?}")
    }
}

#[derive(Debug)]
enum Event {
    Initialize,
    Start,
    Fault,
}

#[derive(Debug, PartialEq)]
enum Command {
    RunDiagnostics,
    BeginProcessing,
    FlushBuffers,
    NotifyOperator,
}

struct Controller;

impl Machine for Controller {
    type Mode = Mode;
    type Event = Event;
    type Command = Command;

    fn initial_mode(&self) -> Mode {
        Mode::Ready
    }

    fn on_event(
        &self,
        mode: &Self::Mode,
        event: &Self::Event,
    ) -> Decision<Self::Mode, Self::Command> {
        use Command::*;
        use Event::*;
        use Mode::*;

        match (mode, event) {
            (Ready, Initialize) => stay().emit(RunDiagnostics),
            (Ready, Start) => transition(Active).emit(BeginProcessing),
            (Active, Fault) => transition(Safe).emit_all([FlushBuffers, NotifyOperator]),
            _ => ignore(),
        }
    }
}

fn main() {
    let controller = Controller;
    let mut mode = controller.initial_mode();

    let events = [Event::Initialize, Event::Start, Event::Fault];

    for event in &events {
        let decision = controller.decide(&mode, event);
        println!("{mode:?} + {event:?} => {decision}");

        let (next_mode, commands) = decision.apply(mode);
        if !commands.is_empty() {
            println!("  commands: {commands:?}");
        }

        mode = next_mode;
    }

    assert_eq!(mode, Mode::Safe);
}