Skip to main content

Machine

Trait Machine 

Source
pub trait Machine<E = Infallible> {
    type Mode;
    type Event;
    type Command;

    // Required methods
    fn initial_mode(&self) -> Self::Mode;
    fn on_event(
        &self,
        mode: &Self::Mode,
        event: &Self::Event,
    ) -> Decision<Self::Mode, Self::Command, E>;

    // Provided methods
    fn decide(
        &self,
        mode: &Self::Mode,
        event: &Self::Event,
    ) -> Decision<Self::Mode, Self::Command, E> { ... }
    fn step(
        &self,
        mode: Self::Mode,
        event: &Self::Event,
    ) -> (Self::Mode, Vec<Self::Command>) { ... }
    fn step_checked<P>(
        &self,
        mode: Self::Mode,
        event: &Self::Event,
        policy: &P,
    ) -> Result<(Self::Mode, Vec<Self::Command>), LifecycleError<Self::Mode>>
       where P: Policy<Self::Mode> { ... }
}
Expand description

A pure function from (mode, event) to Decision.

This is the central trait of the lifecycle engine. Implementations define how the system responds to events in each mode. The trait is deliberately minimal:

  • No self mutation: on_event takes &self, ensuring the machine logic is pure and deterministic.
  • No time parameter: time composes via the runtime and the time feature. The base trait stays usable without it.
  • No I/O: the machine never performs side effects. It returns a Decision that the runtime interprets.

§Associated Types

  • Mode: the set of lifecycle phases (e.g., Ready, Active, Safe).
  • Event: external stimuli the system can receive.
  • Command: instructions for the runtime to execute.

§Type Parameter

  • E: domain errors that can occur during event processing. Defaults to Infallible.

§Examples

use ready_active_safe::prelude::*;

struct Echo;

impl Machine for Echo {
    type Mode = ();
    type Event = String;
    type Command = String;

    fn initial_mode(&self) {}

    fn on_event(&self, _mode: &(), event: &String) -> Decision<(), String> {
        Decision::stay().with_command(event.clone())
    }
}

let echo = Echo;
let event = "hello".to_string();
let d = echo.on_event(&(), &event);
assert_eq!(d.commands(), &[event]);

Required Associated Types§

Source

type Mode

The set of lifecycle modes.

Source

type Event

External events that drive the system.

Source

type Command

Commands emitted for the runtime to execute.

Required Methods§

Source

fn initial_mode(&self) -> Self::Mode

Returns the starting mode for this lifecycle.

Every lifecycle has a well-defined starting point. Declaring it here keeps the definition self-contained instead of requiring the caller to know the right value.

§Examples
use ready_active_safe::prelude::*;

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

struct System;

impl Machine for System {
    type Mode = Mode;
    type Event = ();
    type Command = ();

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

    fn on_event(&self, _: &Mode, _: &()) -> Decision<Mode, ()> {
        stay()
    }
}

let system = System;
assert_eq!(system.initial_mode(), Mode::Ready);
Source

fn on_event( &self, mode: &Self::Mode, event: &Self::Event, ) -> Decision<Self::Mode, Self::Command, E>

Evaluates an event in the context of the current mode.

Returns a Decision describing whether the system should change mode and what commands to emit.

Provided Methods§

Source

fn decide( &self, mode: &Self::Mode, event: &Self::Event, ) -> Decision<Self::Mode, Self::Command, E>

Alias for Machine::on_event.

This reads well in runtimes and tests.

Examples found in repository?
examples/basic.rs (line 74)
67fn main() {
68    let controller = Controller;
69    let mut mode = controller.initial_mode();
70
71    let events = [Event::Initialize, Event::Start, Event::Fault];
72
73    for event in &events {
74        let decision = controller.decide(&mode, event);
75        println!("{mode:?} + {event:?} => {decision}");
76
77        let (next_mode, commands) = decision.apply(mode);
78        if !commands.is_empty() {
79            println!("  commands: {commands:?}");
80        }
81
82        mode = next_mode;
83    }
84
85    assert_eq!(mode, Mode::Safe);
86}
More examples
Hide additional examples
examples/openxr_session.rs (line 116)
101fn main() {
102    let session = XrSession;
103    let mut mode = session.initial_mode();
104
105    let events = [
106        XrEvent::SessionStateChanged(SessionMode::Ready),
107        XrEvent::SessionStateChanged(SessionMode::Synchronized),
108        XrEvent::SessionStateChanged(SessionMode::Visible),
109        XrEvent::SessionStateChanged(SessionMode::Focused),
110        XrEvent::FrameReady,
111        XrEvent::FrameReady,
112        XrEvent::RequestExit,
113    ];
114
115    for event in &events {
116        let decision = session.decide(&mode, event);
117        println!("{mode:?} + {event:?} => {decision}");
118
119        let (next_mode, commands) = decision.apply(mode);
120        if !commands.is_empty() {
121            println!("  commands: {commands:?}");
122        }
123
124        mode = next_mode;
125    }
126
127    assert_eq!(mode, SessionMode::Stopping);
128}
examples/recovery.rs (line 113)
97fn main() -> Result<(), LifecycleError<Mode>> {
98    let controller = SafetyController;
99    let policy = LifecyclePolicy;
100    let mut mode = controller.initial_mode();
101
102    let events = [
103        Event::Activate,
104        Event::TransientFault,
105        Event::CriticalFault,
106        Event::RecoveryApproved,
107        Event::DiagnosticsComplete,
108        Event::Activate,
109        Event::Shutdown,
110    ];
111
112    for event in &events {
113        let decision = controller.decide(&mode, event);
114        println!("{mode:?} + {event:?} => {decision}");
115
116        match decision.apply_checked(mode, &policy) {
117            Ok((next_mode, commands)) => {
118                if !commands.is_empty() {
119                    println!("  commands: {commands:?}");
120                }
121                mode = next_mode;
122            }
123            Err(LifecycleError::TransitionDenied { from, to, reason }) => {
124                println!("  denied: {from:?} -> {to:?} ({reason})");
125                mode = from;
126            }
127            Err(err) => return Err(err),
128        }
129    }
130
131    assert!(!policy.is_allowed(&Mode::Active, &Mode::Ready));
132    assert!(!policy.is_allowed(&Mode::Safe, &Mode::Active));
133    assert_eq!(mode, Mode::Safe);
134
135    Ok(())
136}
Source

fn step( &self, mode: Self::Mode, event: &Self::Event, ) -> (Self::Mode, Vec<Self::Command>)

Returns the next mode and commands for an owned current mode.

This is a convenience for runtimes that own the mode value. It is equivalent to calling decide and then Decision::apply.

Source

fn step_checked<P>( &self, mode: Self::Mode, event: &Self::Event, policy: &P, ) -> Result<(Self::Mode, Vec<Self::Command>), LifecycleError<Self::Mode>>
where P: Policy<Self::Mode>,

Returns the next mode and commands after checking a policy.

This is a convenience for runtimes that want policy checks to be explicit and centralized. It is equivalent to calling decide and then Decision::apply_checked.

§Errors

Returns LifecycleError::TransitionDenied if the policy rejects the requested mode change.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§