pipecircuit 0.0.6

Correct. Robust. Fast. Portable.
Documentation
pub use crate::Transport;

use crate::{
    Circuit, Participant, StateMachine, StepInputOf, Switchboard, TurnContext, TurnObserver,
};

mod private {
    use super::{Participant, StateMachine, StepInputOf};

    pub(super) trait StepLaw<'message> {}

    impl<'message, Machine> StepLaw<'message> for Machine
    where
        Machine: StateMachine + Participant,
        Machine::In<'message>: StepInputOf<Machine, Outcome = Machine::Out<'message>>,
    {
    }
}

#[doc(hidden)]
#[allow(private_bounds)]
pub trait StepsParticipantInputToOutput<'message>:
    StateMachine + Participant + private::StepLaw<'message>
{
    #[must_use = "participant step outcomes must be routed or settled"]
    #[doc(hidden)]
    fn step_boundary<Observer: TurnObserver>(
        &mut self,
        input: Self::In<'message>,
        turn: &mut TurnContext<Observer>,
    ) -> Self::Out<'message>;
}

impl<'message, Machine> StepsParticipantInputToOutput<'message> for Machine
where
    Machine: StateMachine + Participant,
    Machine::In<'message>: StepInputOf<Machine, Outcome = Machine::Out<'message>>,
{
    #[inline(always)]
    fn step_boundary<Observer: TurnObserver>(
        &mut self,
        input: Self::In<'message>,
        turn: &mut TurnContext<Observer>,
    ) -> Self::Out<'message> {
        turn.step(self, input)
    }
}

pub struct Inline<Machine> {
    destination: Machine,
}

impl<Machine> Inline<Machine> {
    #[inline(always)]
    pub const fn new(destination: Machine) -> Self {
        Self { destination }
    }

    #[inline(always)]
    pub const fn destination(&self) -> &Machine {
        &self.destination
    }

    #[inline(always)]
    pub fn into_destination(self) -> Machine {
        self.destination
    }
}

impl<Machine> Transport<Machine> for Inline<Machine>
where
    Machine: for<'message> StepsParticipantInputToOutput<'message>,
{
    type Outcome<'transport, 'message>
        = Machine::Out<'message>
    where
        Self: 'transport;

    #[inline(always)]
    fn transport<'transport, 'message>(
        &'transport mut self,
        input: Machine::In<'message>,
        turn: &mut TurnContext<impl TurnObserver>,
    ) -> Self::Outcome<'transport, 'message> {
        self.destination.step_boundary(input, turn)
    }
}

pub struct InlineSwitchboard<Board> {
    destination: Board,
}

impl<Board> InlineSwitchboard<Board> {
    #[inline(always)]
    pub const fn new(destination: Board) -> Self {
        Self { destination }
    }

    #[inline(always)]
    pub const fn destination(&self) -> &Board {
        &self.destination
    }

    #[inline(always)]
    pub fn into_destination(self) -> Board {
        self.destination
    }
}

impl<Board: Switchboard> Transport<Board> for InlineSwitchboard<Board> {
    type Outcome<'transport, 'message>
        = Board::Out<'message>
    where
        Self: 'transport;

    #[inline(always)]
    fn transport<'transport, 'message>(
        &'transport mut self,
        input: Board::In<'message>,
        turn: &mut TurnContext<impl TurnObserver>,
    ) -> Self::Outcome<'transport, 'message> {
        turn.route(&mut self.destination, input)
    }
}

pub struct InlineCircuit<InstalledCircuit> {
    destination: InstalledCircuit,
}

impl<InstalledCircuit> InlineCircuit<InstalledCircuit> {
    #[inline(always)]
    pub const fn new(destination: InstalledCircuit) -> Self {
        Self { destination }
    }

    #[inline(always)]
    pub const fn destination(&self) -> &InstalledCircuit {
        &self.destination
    }

    #[inline(always)]
    pub fn into_destination(self) -> InstalledCircuit {
        self.destination
    }
}

impl<InstalledCircuit: Circuit> Transport<InstalledCircuit> for InlineCircuit<InstalledCircuit> {
    type Outcome<'transport, 'message>
        = InstalledCircuit::Out<'message>
    where
        Self: 'transport;

    #[inline(always)]
    fn transport<'transport, 'message>(
        &'transport mut self,
        input: InstalledCircuit::In<'message>,
        turn: &mut TurnContext<impl TurnObserver>,
    ) -> Self::Outcome<'transport, 'message> {
        self.destination.turn_in_context(input, turn)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{NoopTurnObserver, TurnContext};

    struct Counter(u8);

    impl StateMachine for Counter {}

    struct Add(u8);

    impl Participant for Counter {
        type In<'message> = Add;
        type Out<'message> = u8;
    }

    impl StepInputOf<Counter> for Add {
        type Outcome = u8;

        fn step(self, counter: &mut Counter) -> Self::Outcome {
            counter.0 = counter.0.saturating_add(self.0);
            counter.0
        }
    }

    #[test]
    fn inline_transport_completes_the_precise_step() {
        let mut transport = Inline::new(Counter(3));
        let mut turn = TurnContext::new(NoopTurnObserver);

        assert_eq!(
            Transport::<Counter>::transport(&mut transport, Add(4), &mut turn),
            7
        );
        assert_eq!(transport.into_destination().0, 7);
    }
}