use crate::{Circuit, CircuitReactionOutcome, Reactor, Switchboard};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ConductionFailure<ReactorFailure, CircuitFailure> {
Reactor { failure: ReactorFailure },
Circuit { failure: CircuitFailure },
}
pub type ConductionResult<Completion, ReactorFailure, CircuitFailure> =
Result<Completion, ConductionFailure<ReactorFailure, CircuitFailure>>;
pub struct Pipecircuit<Board, InstalledCircuit>
where
Board: Switchboard,
InstalledCircuit: Circuit<Board>,
{
switchboard: Board,
circuit: InstalledCircuit,
}
impl<Board, InstalledCircuit> Pipecircuit<Board, InstalledCircuit>
where
Board: Switchboard,
InstalledCircuit: Circuit<Board>,
{
pub const fn new(switchboard: Board, circuit: InstalledCircuit) -> Self {
Self {
switchboard,
circuit,
}
}
}
impl<Board, InstalledCircuit> Pipecircuit<Board, InstalledCircuit>
where
Board: Switchboard,
InstalledCircuit: Circuit<Board>,
{
pub const fn switchboard(&self) -> &Board {
&self.switchboard
}
pub const fn switchboard_mut(&mut self) -> &mut Board {
&mut self.switchboard
}
pub const fn circuit(&self) -> &InstalledCircuit {
&self.circuit
}
pub const fn circuit_mut(&mut self) -> &mut InstalledCircuit {
&mut self.circuit
}
pub fn into_parts(self) -> (Board, InstalledCircuit) {
(self.switchboard, self.circuit)
}
pub fn conduct(
&mut self,
) -> ConductionResult<
InstalledCircuit::Completion,
<InstalledCircuit::Reactor as Reactor>::Failure,
InstalledCircuit::Failure,
> {
loop {
let reaction = self
.circuit
.reactor()
.wait_for_reaction()
.map_err(|failure| ConductionFailure::Reactor { failure })?;
loop {
match self
.circuit
.react(&mut self.switchboard, &reaction)
.map_err(|failure| ConductionFailure::Circuit { failure })?
{
CircuitReactionOutcome::ContinueReaction => {}
CircuitReactionOutcome::AwaitingReaction => break,
CircuitReactionOutcome::Completed { completion } => return Ok(completion),
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Eq, PartialEq)]
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))
}
}
#[derive(Debug, Eq, PartialEq)]
struct TestReactor {
next_input: u8,
waits: u8,
}
impl Reactor for TestReactor {
type Reaction = u8;
type Failure = core::convert::Infallible;
fn wait_for_reaction(&mut self) -> Result<Self::Reaction, Self::Failure> {
self.waits = self.waits.saturating_add(1);
Ok(self.next_input)
}
}
#[derive(Debug, Eq, PartialEq)]
struct InstalledCircuit {
reactor: TestReactor,
reactions: u8,
}
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> {
self.reactions = self.reactions.saturating_add(1);
if self.reactions == 1 {
return Ok(CircuitReactionOutcome::ContinueReaction);
}
Ok(CircuitReactionOutcome::Completed {
completion: switchboard.route(*reaction).into_single()?,
})
}
}
#[test]
fn a_pipecircuit_owns_one_matching_switchboard_and_circuit() {
let mut program = Pipecircuit::new(
Router,
InstalledCircuit {
reactor: TestReactor {
next_input: 7,
waits: 0,
},
reactions: 0,
},
);
assert_eq!(program.conduct(), Ok(7));
assert_eq!(program.switchboard(), &Router);
assert_eq!(*program.switchboard_mut(), Router);
assert_eq!(
program.circuit(),
&InstalledCircuit {
reactor: TestReactor {
next_input: 7,
waits: 1,
},
reactions: 2,
}
);
assert_eq!(
program.circuit_mut(),
&InstalledCircuit {
reactor: TestReactor {
next_input: 7,
waits: 1,
},
reactions: 2,
}
);
let (switchboard, circuit) = program.into_parts();
assert_eq!(switchboard, Router);
assert_eq!(
circuit,
InstalledCircuit {
reactor: TestReactor {
next_input: 7,
waits: 1,
},
reactions: 2,
}
);
}
}