pipecircuit 0.0.23

Correct. Robust. Fast. Portable. Pick four.
Documentation
use core::marker::PhantomData;

use crate::Pipe;

/// A lifetime-indexed family of values that may be encoded or decoded.
pub trait MessageFamily {
    type Message<'message>;
}

/// Selects the message family accepted by an input pipe.
pub struct PipeIn<Selected: Pipe>(PhantomData<fn() -> Selected>);

impl<Selected: Pipe> MessageFamily for PipeIn<Selected> {
    type Message<'message> = Selected::In<'message>;
}

pub struct PipeOut<Selected: Pipe>(PhantomData<fn() -> Selected>);

impl<Selected: Pipe> MessageFamily for PipeOut<Selected> {
    type Message<'message> = Selected::Out<'message>;
}

/// Selects one owned message type as a lifetime-indexed family.
pub struct OwnedMessage<Message>(PhantomData<fn() -> Message>);

impl<Message> MessageFamily for OwnedMessage<Message> {
    type Message<'message> = Message;
}

/// The byte count written by a message encoder.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct EncodedByteCount(usize);

impl EncodedByteCount {
    pub const fn new(value: usize) -> Self {
        Self(value)
    }

    pub const fn get(self) -> usize {
        self.0
    }
}

/// Proves that one lifetime-indexed message family can be serialized into
/// bounded caller-owned storage.
pub trait MessageEncoder {
    type Message<'message>: ?Sized;
    type Error;

    fn encode<'message>(
        &self,
        message: &Self::Message<'message>,
        destination: &mut [u8],
    ) -> Result<EncodedByteCount, Self::Error>;
}

/// Proves that one serialized message can become a typed value.
///
/// The decoded value may borrow from the reusable receive frame.
pub trait MessageDecoder {
    type Message<'frame>;
    type Error;

    fn decode<'frame>(&self, frame: &'frame mut [u8])
    -> Result<Self::Message<'frame>, Self::Error>;
}

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

    struct Duplex;

    impl Pipe for Duplex {
        type In<'message> = &'message str;
        type Out<'message> = &'message [u8];
    }

    struct Source;

    impl Pipe for Source {
        type In<'message> = NoInput;
        type Out<'message> = &'message str;
    }

    struct Sink;

    impl Pipe for Sink {
        type In<'message> = &'message str;
        type Out<'message> = NoOutput;
    }

    #[test]
    fn participant_message_families_include_uninhabited_directions() {
        fn assert_message_families<Selected>()
        where
            Selected: Pipe,
            for<'message> PipeIn<Selected>:
                MessageFamily<Message<'message> = Selected::In<'message>>,
            for<'message> PipeOut<Selected>:
                MessageFamily<Message<'message> = Selected::Out<'message>>,
        {
        }

        assert_message_families::<Duplex>();
        assert_message_families::<Source>();
        assert_message_families::<Sink>();
    }

    #[test]
    fn encoded_byte_counts_preserve_their_value() {
        assert_eq!(EncodedByteCount::new(7).get(), 7);
    }
}