use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::sync::Arc;
use core::future::Future;
use core::pin::Pin;
use proxima_core::ProximaError;
use crate::pipe::primitives::{Pipe, SendPipe};
pub type BoxFuture<'a, Output> = Pin<Box<dyn Future<Output = Output> + 'a>>;
pub type SendBoxFuture<'a, Output> = Pin<Box<dyn Future<Output = Output> + Send + 'a>>;
pub trait DynPipe<In, Out> {
fn call_dyn(&self, input: In) -> BoxFuture<'_, Result<Out, ProximaError>>;
}
impl<P> DynPipe<P::In, P::Out> for P
where
P: Pipe,
ProximaError: From<P::Err>,
{
fn call_dyn(&self, input: P::In) -> BoxFuture<'_, Result<P::Out, ProximaError>> {
Box::pin(async move { self.call(input).await.map_err(ProximaError::from) })
}
}
pub type LocalPipeHandle<In, Out> = Rc<dyn DynPipe<In, Out>>;
pub fn into_local_handle<P>(pipe: P) -> LocalPipeHandle<P::In, P::Out>
where
P: Pipe + 'static,
ProximaError: From<P::Err>,
{
Rc::new(pipe)
}
pub trait SendDynPipe<In, Out>: Send + Sync {
fn call_dyn(&self, input: In) -> SendBoxFuture<'_, Result<Out, ProximaError>>;
}
impl<P> SendDynPipe<P::In, P::Out> for P
where
P: SendPipe,
P::In: Send,
ProximaError: From<P::Err>,
{
fn call_dyn(&self, input: P::In) -> SendBoxFuture<'_, Result<P::Out, ProximaError>> {
Box::pin(async move { self.call(input).await.map_err(ProximaError::from) })
}
}
pub type PipeHandle<In, Out> = Arc<dyn SendDynPipe<In, Out>>;
pub fn into_handle<P>(pipe: P) -> PipeHandle<P::In, P::Out>
where
P: SendPipe,
P::In: Send,
ProximaError: From<P::Err>,
{
Arc::new(pipe)
}
impl<In: 'static, Out: 'static> SendPipe for dyn SendDynPipe<In, Out> {
type In = In;
type Out = Out;
type Err = ProximaError;
fn call(&self, input: In) -> impl Future<Output = Result<Out, ProximaError>> + Send {
self.call_dyn(input)
}
}
impl<In: 'static, Out: 'static> Pipe for dyn SendDynPipe<In, Out> {
type In = In;
type Out = Out;
type Err = ProximaError;
fn call(&self, input: In) -> impl Future<Output = Result<Out, ProximaError>> {
self.call_dyn(input)
}
}
impl<In, Out> Pipe for dyn DynPipe<In, Out> {
type In = In;
type Out = Out;
type Err = ProximaError;
fn call(&self, input: In) -> impl Future<Output = Result<Out, ProximaError>> {
self.call_dyn(input)
}
}
impl<Inner> SendPipe for Arc<Inner>
where
Inner: SendPipe + ?Sized,
{
type In = Inner::In;
type Out = Inner::Out;
type Err = Inner::Err;
fn call(
&self,
input: Self::In,
) -> impl core::future::Future<Output = Result<Self::Out, Self::Err>> + Send {
Inner::call(self, input)
}
}
impl<Inner> Pipe for Arc<Inner>
where
Inner: Pipe + ?Sized,
{
type In = Inner::In;
type Out = Inner::Out;
type Err = Inner::Err;
fn call(
&self,
input: Self::In,
) -> impl core::future::Future<Output = Result<Self::Out, Self::Err>> {
Inner::call(self, input)
}
}
impl<Inner> Pipe for Rc<Inner>
where
Inner: Pipe + ?Sized,
{
type In = Inner::In;
type Out = Inner::Out;
type Err = Inner::Err;
fn call(
&self,
input: Self::In,
) -> impl core::future::Future<Output = Result<Self::Out, Self::Err>> {
Inner::call(self, input)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::{SendDynPipe, into_handle, into_local_handle};
use crate::block_on;
use crate::pipe::primitives::{Pipe, SendPipe};
use core::future::Future;
struct Tagger(u64);
impl Pipe for Tagger {
type In = u64;
type Out = u64;
type Err = proxima_core::ProximaError;
fn call(
&self,
input: u64,
) -> impl Future<Output = Result<u64, proxima_core::ProximaError>> {
let tag = self.0;
async move { Ok(input + tag) }
}
}
impl SendPipe for Tagger {
type In = u64;
type Out = u64;
type Err = proxima_core::ProximaError;
fn call(
&self,
input: u64,
) -> impl Future<Output = Result<u64, proxima_core::ProximaError>> + Send {
let tag = self.0;
async move { Ok(input + tag) }
}
}
#[test]
fn local_handle_erases_a_no_send_pipe() {
let handle = into_local_handle(Tagger(10));
let out = block_on(handle.call_dyn(5)).expect("erased local dispatch");
assert_eq!(out, 15);
}
#[test]
fn send_handle_erases_a_cross_core_pipe() {
let handle = into_handle(Tagger(100));
let out =
block_on(SendDynPipe::call_dyn(handle.as_ref(), 5)).expect("erased send dispatch");
assert_eq!(out, 105);
}
fn assert_send_sync<Type: Send + Sync>(_: &Type) {}
#[test]
fn send_handle_is_send_and_sync() {
let handle = into_handle(Tagger(1));
assert_send_sync(&handle);
}
}