rumpsteak 0.1.0

Session types for asynchronous communication between multiple parties.
Documentation
pub mod channel;
pub mod choice;
pub mod role;

pub use self::channel::SendError;
pub use rumpsteak_macros::{IntoSession, Label};

use self::{
    choice::{External, Internal},
    role::{Receiver, Role, Route, Sender},
};
use futures::FutureExt;
use std::{convert::Infallible, future::Future, marker::PhantomData, result};
use thiserror::Error;

pub type Result<T, E = Error> = result::Result<T, E>;

#[derive(Debug, Error)]
pub enum Error {
    #[error(transparent)]
    Send(#[from] SendError),
    #[error(transparent)]
    Receive(#[from] ReceiveError),
}

#[derive(Debug, Error)]
pub enum ReceiveError {
    #[error(transparent)]
    Channel(#[from] channel::ReceiveError),
    #[error("received message with an unexpected type")]
    UnexpectedType,
}

pub trait Label<L> {
    fn wrap(label: L) -> Self;

    fn unwrap(self) -> Option<L>;
}

pub struct State<'r, R: Role>(&'r mut R);

impl<'r, R: Role> State<'r, R> {
    pub fn into_session<S: Session<'r, R>>(self) -> S {
        S::from_state(self)
    }
}

pub trait Session<'r, R: Role> {
    fn from_state(state: State<'r, R>) -> Self;
}

pub trait IntoSession<'r, R: Role>: Session<'r, R> {
    type Session: Session<'r, R>;

    fn into_session(self) -> Self::Session;
}

pub struct End<'r> {
    phantom: PhantomData<&'r ()>,
}

impl<'r, R: Role> Session<'r, R> for End<'r> {
    #[inline]
    fn from_state(_: State<'r, R>) -> Self {
        Self {
            phantom: PhantomData,
        }
    }
}

pub struct Send<'q, Q: Route<R>, R: Route<Q>, L, S: Session<'q, Q>>
where
    Q::Message: Label<L>,
    Q::Route: Sender<R>,
    R: Role<Message = Q::Message>,
    R::Route: Receiver<Q>,
{
    state: State<'q, Q>,
    phantom: PhantomData<(R, L, S)>,
}

impl<'q, Q: Route<R>, R: Route<Q>, L, S: Session<'q, Q>> Session<'q, Q> for Send<'q, Q, R, L, S>
where
    Q::Message: Label<L>,
    Q::Route: Sender<R>,
    R: Role<Message = Q::Message>,
    R::Route: Receiver<Q>,
{
    #[inline]
    fn from_state(state: State<'q, Q>) -> Self {
        Self {
            state,
            phantom: PhantomData,
        }
    }
}

impl<'q, Q: Route<R>, R: Route<Q>, L, S: Session<'q, Q>> Send<'q, Q, R, L, S>
where
    Q::Message: Label<L>,
    Q::Route: Sender<R>,
    R: Role<Message = Q::Message>,
    R::Route: Receiver<Q>,
{
    #[inline]
    pub fn send(self, label: L) -> Result<S, SendError> {
        self.state.0.route().sender().send(Label::wrap(label))?;
        Ok(self.state.into_session())
    }
}

pub struct Receive<'q, Q: Route<R>, R: Route<Q>, L, S: Session<'q, Q>>
where
    Q::Message: Label<L>,
    Q::Route: Receiver<R>,
    R: Role<Message = Q::Message>,
    R::Route: Sender<Q>,
{
    state: State<'q, Q>,
    phantom: PhantomData<(R, L, S)>,
}

impl<'q, Q: Route<R>, R: Route<Q>, L, S: Session<'q, Q>> Session<'q, Q> for Receive<'q, Q, R, L, S>
where
    Q::Message: Label<L>,
    Q::Route: Receiver<R>,
    R: Role<Message = Q::Message>,
    R::Route: Sender<Q>,
{
    #[inline]
    fn from_state(state: State<'q, Q>) -> Self {
        Self {
            state,
            phantom: PhantomData,
        }
    }
}

impl<'q, Q: Route<R>, R: Route<Q>, L, S: Session<'q, Q>> Receive<'q, Q, R, L, S>
where
    Q::Message: Label<L>,
    Q::Route: Receiver<R>,
    R: Role<Message = Q::Message>,
    R::Route: Sender<Q>,
{
    #[inline]
    pub async fn receive(self) -> Result<(L, S), ReceiveError> {
        let label = Label::unwrap(self.state.0.route().receiver().receive().await?);
        let label = label.ok_or(ReceiveError::UnexpectedType)?;
        Ok((label, self.state.into_session()))
    }
}

pub struct Select<'q, Q: Route<R>, R: Route<Q>, C>
where
    Q::Route: Sender<R>,
    R: Role<Message = Q::Message>,
    R::Route: Receiver<Q>,
{
    state: State<'q, Q>,
    phantom: PhantomData<(R, C)>,
}

impl<'q, Q: Route<R>, R: Route<Q>, C> Session<'q, Q> for Select<'q, Q, R, C>
where
    Q::Route: Sender<R>,
    R: Role<Message = Q::Message>,
    R::Route: Receiver<Q>,
{
    #[inline]
    fn from_state(state: State<'q, Q>) -> Self {
        Self {
            state,
            phantom: PhantomData,
        }
    }
}

impl<'q, Q: Route<R>, R: Route<Q>, C> Select<'q, Q, R, C>
where
    Q::Route: Sender<R>,
    R: Role<Message = Q::Message>,
    R::Route: Receiver<Q>,
{
    #[inline]
    pub fn select<L>(self, label: L) -> Result<C::Session, SendError>
    where
        Q::Message: Label<L>,
        C: Internal<'q, Q, L>,
    {
        self.state.0.route().sender().send(Label::wrap(label))?;
        Ok(self.state.into_session())
    }
}

pub struct Branch<'q, Q: Route<R>, R: Route<Q>, C: External<'q, Q>>
where
    Q::Route: Receiver<R>,
    R: Role<Message = Q::Message>,
    R::Route: Sender<Q>,
{
    state: State<'q, Q>,
    phantom: PhantomData<(R, C)>,
}

impl<'q, Q: Route<R>, R: Route<Q>, C: External<'q, Q>> Session<'q, Q> for Branch<'q, Q, R, C>
where
    Q::Route: Receiver<R>,
    R: Role<Message = Q::Message>,
    R::Route: Sender<Q>,
{
    #[inline]
    fn from_state(state: State<'q, Q>) -> Self {
        Self {
            state,
            phantom: PhantomData,
        }
    }
}

impl<'q, Q: Route<R>, R: Route<Q>, C: External<'q, Q>> Branch<'q, Q, R, C>
where
    Q::Route: Receiver<R>,
    R: Role<Message = Q::Message>,
    R::Route: Sender<Q>,
{
    #[inline]
    pub async fn branch(self) -> Result<C, ReceiveError> {
        let message = self.state.0.route().receiver().receive().await?;
        C::choice(self.state, message).ok_or(ReceiveError::UnexpectedType)
    }
}

#[inline]
pub async fn session<'r, R: Role, S: Session<'r, R>, T, F>(
    role: &'r mut R,
    f: impl FnOnce(S) -> F,
) -> T
where
    F: Future<Output = (T, End<'r>)>,
{
    let output = try_session(role, |s| f(s).map(Ok)).await;
    output.unwrap_or_else(|infallible: Infallible| match infallible {})
}

#[inline]
pub async fn try_session<'r, R: Role, S: Session<'r, R>, T, E, F>(
    role: &'r mut R,
    f: impl FnOnce(S) -> F,
) -> Result<T, E>
where
    F: Future<Output = Result<(T, End<'r>), E>>,
{
    let state = State(role);
    f(state.into_session()).await.map(|(output, _)| output)
}