use alloc::vec::Vec;
use crate::{LifecycleError, Machine, Policy};
#[derive(Debug)]
pub struct Runner<'a, M, E = core::convert::Infallible>
where
M: Machine<E>,
{
machine: &'a M,
mode: M::Mode,
}
impl<'a, M, E> Runner<'a, M, E>
where
M: Machine<E>,
{
#[must_use]
pub fn new(machine: &'a M) -> Self {
Self {
mode: machine.initial_mode(),
machine,
}
}
#[must_use]
pub const fn with_mode(machine: &'a M, mode: M::Mode) -> Self {
Self { machine, mode }
}
#[must_use]
pub const fn mode(&self) -> &M::Mode {
&self.mode
}
#[must_use]
pub fn feed(&mut self, event: &M::Event) -> Vec<M::Command> {
let decision = self.machine.decide(&self.mode, event);
let (change, commands) = decision.into_parts();
if let Some(change) = change {
self.mode = change.target;
}
commands
}
pub fn feed_checked<P>(
&mut self,
event: &M::Event,
policy: &P,
) -> Result<Vec<M::Command>, LifecycleError<M::Mode>>
where
P: Policy<M::Mode>,
M::Mode: Clone,
{
let decision = self.machine.decide(&self.mode, event);
let (change, commands) = decision.into_parts();
match change {
Some(change) => {
let target = change.target;
match policy.check(&self.mode, &target) {
Ok(()) => {
self.mode = target;
Ok(commands)
}
Err(reason) => Err(LifecycleError::TransitionDenied {
from: self.mode.clone(),
to: target,
reason,
}),
}
}
None => Ok(commands),
}
}
pub fn feed_and_dispatch<F>(&mut self, event: &M::Event, mut dispatch: F)
where
F: FnMut(M::Command),
{
for command in self.feed(event) {
dispatch(command);
}
}
pub fn feed_checked_and_dispatch<P, F>(
&mut self,
event: &M::Event,
policy: &P,
mut dispatch: F,
) -> Result<(), LifecycleError<M::Mode>>
where
P: Policy<M::Mode>,
M::Mode: Clone,
F: FnMut(M::Command),
{
for command in self.feed_checked(event, policy)? {
dispatch(command);
}
Ok(())
}
}