Skip to main content

runner/
runner.rs

1//! Runner with policy enforcement.
2//!
3//! Demonstrates the `runtime::Runner` event loop with a forward-only
4//! policy. Shows what happens when a transition is allowed vs denied.
5
6#![allow(clippy::print_stdout, clippy::enum_glob_use)]
7
8use ready_active_safe::prelude::*;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11enum Mode {
12    Ready,
13    Active,
14    Safe,
15}
16
17impl core::fmt::Display for Mode {
18    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19        write!(f, "{self:?}")
20    }
21}
22
23#[derive(Debug)]
24enum Event {
25    Start,
26    Reset,
27    Fault,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31enum Command {
32    Init,
33    Shutdown,
34}
35
36struct System;
37
38impl Machine for System {
39    type Mode = Mode;
40    type Event = Event;
41    type Command = Command;
42
43    fn initial_mode(&self) -> Mode {
44        Mode::Ready
45    }
46
47    fn on_event(&self, mode: &Mode, event: &Event) -> Decision<Mode, Command> {
48        use Command::*;
49        use Event::*;
50        use Mode::*;
51
52        match (mode, event) {
53            (Ready, Start) => transition(Active).emit(Init),
54            (Active, Reset) => transition(Ready),
55            (Active, Fault) => transition(Safe).emit(Shutdown),
56            _ => ignore(),
57        }
58    }
59}
60
61/// Only allows forward progression: Ready → Active → Safe.
62struct ForwardOnly;
63
64impl Policy<Mode> for ForwardOnly {
65    fn is_allowed(&self, from: &Mode, to: &Mode) -> bool {
66        matches!(
67            (from, to),
68            (Mode::Ready, Mode::Active) | (Mode::Active, Mode::Safe)
69        )
70    }
71}
72
73fn main() {
74    let system = System;
75    let policy = ForwardOnly;
76    let mut runner = Runner::new(&system);
77
78    println!("Starting in {:?}", runner.mode());
79
80    // Allowed: Ready → Active
81    let commands = runner.feed_checked(&Event::Start, &policy);
82    println!("\nEvent: Start");
83    println!("  mode: {:?}, commands: {:?}", runner.mode(), commands);
84
85    // Denied: Active → Ready (policy blocks backward transitions)
86    let result = runner.feed_checked(&Event::Reset, &policy);
87    println!("\nEvent: Reset");
88    match &result {
89        Ok(cmds) => println!("  mode: {:?}, commands: {cmds:?}", runner.mode()),
90        Err(err) => println!("  denied: {err}"),
91    }
92    println!("  mode unchanged: {:?}", runner.mode());
93
94    // Allowed: Active → Safe
95    let commands = runner.feed_checked(&Event::Fault, &policy);
96    println!("\nEvent: Fault");
97    println!("  mode: {:?}, commands: {:?}", runner.mode(), commands);
98}