pipecircuit 0.0.18

Correct. Robust. Fast. Portable. Pick four.
Documentation
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);
    }
}