use crate::{Reactor, Switchboard};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use = "circuit reaction outcomes must be handled"]
pub enum CircuitReactionOutcome<Completion> {
ContinueReaction,
AwaitingReaction,
Completed { completion: Completion },
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use = "circuit send outcomes must be handled"]
pub enum CircuitSendOutcome<From, Completion> {
Immediate { from: From },
Submitted,
Completed { completion: Completion },
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use = "circuit flow outcomes must be handled"]
pub enum CircuitFlowOutcome<Completion> {
Submitted,
Completed { completion: Completion },
}
#[inline]
pub fn flow<'message, Board, Completion, Failure>(
switchboard: &mut Board,
mut from: Board::From<'message>,
mut send: impl FnMut(
Board::To<'message>,
) -> Result<CircuitSendOutcome<Board::From<'message>, Completion>, Failure>,
) -> Result<CircuitFlowOutcome<Completion>, Failure>
where
Board: Switchboard,
{
loop {
match send(switchboard.route(from))? {
CircuitSendOutcome::Immediate { from: next } => from = next,
CircuitSendOutcome::Submitted => return Ok(CircuitFlowOutcome::Submitted),
CircuitSendOutcome::Completed { completion } => {
return Ok(CircuitFlowOutcome::Completed { completion });
}
}
}
}
pub trait Circuit<Board: Switchboard> {
type Reactor: Reactor;
type Completion;
type Failure;
fn reactor(&mut self) -> &mut Self::Reactor;
fn react(
&mut self,
switchboard: &mut Board,
reaction: &<Self::Reactor as Reactor>::Reaction,
) -> Result<CircuitReactionOutcome<Self::Completion>, Self::Failure>;
}
#[cfg(test)]
mod tests {
use super::*;
struct Router;
impl Switchboard for Router {
type From<'message> = u8;
type To<'message> = u16;
fn route<'message>(&mut self, input: Self::From<'message>) -> Self::To<'message> {
u16::from(input)
}
}
struct TestReactor;
impl Reactor for TestReactor {
type Reaction = u8;
type Failure = core::convert::Infallible;
fn wait_for_reaction(&mut self) -> Result<Self::Reaction, Self::Failure> {
Ok(7)
}
}
struct InstalledCircuit {
reactor: TestReactor,
}
impl Circuit<Router> for InstalledCircuit {
type Reactor = TestReactor;
type Completion = u16;
type Failure = core::convert::Infallible;
fn reactor(&mut self) -> &mut Self::Reactor {
&mut self.reactor
}
fn react(
&mut self,
switchboard: &mut Router,
reaction: &u8,
) -> Result<CircuitReactionOutcome<Self::Completion>, Self::Failure> {
Ok(CircuitReactionOutcome::Completed {
completion: switchboard.route(*reaction),
})
}
}
#[test]
fn a_circuit_reacts_through_only_its_selected_switchboard() {
let mut circuit = InstalledCircuit {
reactor: TestReactor,
};
let mut switchboard = Router;
assert_eq!(
circuit.react(&mut switchboard, &7),
Ok(CircuitReactionOutcome::Completed { completion: 7 })
);
}
#[test]
fn flow_routes_every_immediate_output_before_submission() {
let mut switchboard = Router;
let mut sent = 0;
assert_eq!(
flow(&mut switchboard, 1, |to| {
sent += 1;
if to < 3 {
let Ok(next) = u8::try_from(to + 1) else {
return Ok(CircuitSendOutcome::Completed { completion: () });
};
Ok::<CircuitSendOutcome<u8, ()>, core::convert::Infallible>(
CircuitSendOutcome::Immediate { from: next },
)
} else {
Ok(CircuitSendOutcome::Submitted)
}
}),
Ok(CircuitFlowOutcome::Submitted)
);
assert_eq!(sent, 3);
}
}