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, const CAPACITY: usize>(
switchboard: &mut Board,
from: Board::From<'message>,
pending: &mut crate::PendingRoutes<Board::To<'message>, CAPACITY>,
mut send: impl FnMut(
Board::To<'message>,
) -> Result<CircuitSendOutcome<Board::From<'message>, Completion>, Failure>,
) -> Result<CircuitFlowOutcome<Completion>, crate::FlowFailure<Failure, Board::To<'message>>>
where
Board: Switchboard,
{
let mut route = switchboard.route(from);
loop {
let next = pending
.admit(route)
.map_err(|rejected| crate::FlowFailure::Capacity {
rejected,
maximum_pending: CAPACITY,
})?;
let destination = match next.or_else(|| pending.pop()) {
Some(destination) => destination,
None => return Ok(CircuitFlowOutcome::Submitted),
};
match send(destination).map_err(crate::FlowFailure::Dispatch)? {
CircuitSendOutcome::Immediate { from } => route = switchboard.route(from),
CircuitSendOutcome::Submitted => route = crate::Route::none(),
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>,
) -> pipecircuit::Route<Self::To<'message>> {
pipecircuit::Route::one(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 = crate::Route<u16>;
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).into_single()?,
})
}
}
#[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,
&mut crate::PendingRoutes::<_, 0>::new(),
|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);
}
}