pipecircuit 0.0.18

Correct. Robust. Fast. Portable. Pick four.
Documentation
pub use crate::{TransportFrom, TransportTo};

use core::marker::PhantomData;

use crate::{CircuitDraft, Pipe, StateMachine, StepInputOf};

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

    pub(super) trait StepLaw<'message, Protocol: Pipe> {}

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

#[doc(hidden)]
#[allow(private_bounds)]
pub trait StepsPipeInputToOutput<'message, Protocol: Pipe>:
    StateMachine + private::StepLaw<'message, Protocol>
{
    #[must_use = "pipe step outcomes must be routed or settled"]
    #[doc(hidden)]
    fn step_boundary(&mut self, input: Protocol::In<'message>) -> Protocol::Out<'message>;
}

impl<'message, Machine, Protocol> StepsPipeInputToOutput<'message, Protocol> for Machine
where
    Machine: StateMachine,
    Protocol: Pipe,
    Protocol::In<'message>: StepInputOf<Machine, Outcome = Protocol::Out<'message>>,
{
    fn step_boundary(&mut self, input: Protocol::In<'message>) -> Protocol::Out<'message> {
        input.step(self)
    }
}

/// A synchronous transport bound to one named pipe and one local implementation.
/// Use `for_pipe` when the protocol is separate from the implementing machine.
pub struct Inline<Implementation, Protocol = Implementation> {
    destination: Implementation,
    protocol: PhantomData<fn() -> Protocol>,
}

impl<Implementation> Inline<Implementation> {
    pub const fn new(destination: Implementation) -> Self {
        Self {
            destination,
            protocol: PhantomData,
        }
    }

    pub const fn for_pipe<Protocol: Pipe>(
        destination: Implementation,
    ) -> Inline<Implementation, Protocol> {
        Inline {
            destination,
            protocol: PhantomData,
        }
    }
}

impl<Implementation, Protocol> Inline<Implementation, Protocol> {
    pub const fn destination(&self) -> &Implementation {
        &self.destination
    }

    pub fn into_destination(self) -> Implementation {
        self.destination
    }
}

impl<Implementation, Protocol> TransportTo<Protocol> for Inline<Implementation, Protocol>
where
    Protocol: Pipe,
    Implementation: for<'message> StepsPipeInputToOutput<'message, Protocol>,
{
    type Outcome<'transport, 'message>
        = Protocol::Out<'message>
    where
        Self: 'transport;

    fn send<'transport, 'message>(
        &'transport mut self,
        input: Protocol::In<'message>,
    ) -> Self::Outcome<'transport, 'message> {
        self.destination.step_boundary(input)
    }
}

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

impl<InstalledCircuit> InlineCircuitDraft<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: CircuitDraft> TransportTo<InstalledCircuit>
    for InlineCircuitDraft<InstalledCircuit>
{
    type Outcome<'transport, 'message>
        = InstalledCircuit::Out<'message>
    where
        Self: 'transport;

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

#[cfg(test)]
mod tests {
    use super::*;
    struct Counter(u8);

    impl StateMachine for Counter {}

    struct Add(u8);

    impl Pipe 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));
        assert_eq!(TransportTo::<Counter>::send(&mut transport, Add(4)), 7);
        assert_eq!(transport.into_destination().0, 7);
    }
}