slim-futures 0.1.0-alpha.0

Asynchronous tools that intends for smaller binary size.
Documentation
use crate::future::map::Map;
use crate::future::try_flatten::TryFlatten;
use crate::support::{FromResidual, RawResidual, Try};
use core::future::{Future, IntoFuture};
use core::ops::ControlFlow;
use core::pin::Pin;
use core::task::{Context, Poll};
use fn_traits::FnMut;
use futures_core::FusedFuture;

#[derive(Clone)]
struct AndThenAsyncFn<F>
where
    F: ?Sized,
{
    f: F,
}

impl<T, F> FnMut<(T,)> for AndThenAsyncFn<F>
where
    T: Try,
    F: FnMut<(T::Output,)> + ?Sized,
    F::Output: IntoFuture,
    <F::Output as IntoFuture>::Output: FromResidual<T::Residual>,
{
    type Output = RawResidual<T::Residual, F::Output>;

    fn call_mut(&mut self, args: (T,)) -> Self::Output {
        match args.0.branch() {
            ControlFlow::Continue(output) => RawResidual::from_output(self.f.call_mut((output,))),
            ControlFlow::Break(residual) => RawResidual::from_residual(residual),
        }
    }
}

pin_project_lite::pin_project! {
    pub struct AndThenAsync<Fut, F>
    where
        Fut: Future,
        Fut::Output: Try,
        F: FnMut<(<Fut::Output as Try>::Output,)>,
        F::Output: IntoFuture,
        <F::Output as IntoFuture>::Output: FromResidual<<Fut::Output as Try>::Residual>,
    {
        #[pin]
        inner: TryFlatten<Map<Fut, AndThenAsyncFn<F>>>
    }
}

impl<Fut, F> AndThenAsync<Fut, F>
where
    Fut: Future,
    Fut::Output: Try,
    F: FnMut<(<Fut::Output as Try>::Output,)>,
    F::Output: IntoFuture,
    <F::Output as IntoFuture>::Output: FromResidual<<Fut::Output as Try>::Residual>,
{
    pub(crate) fn new(fut: Fut, f: F) -> Self {
        Self {
            inner: TryFlatten::new(Map::new(fut, AndThenAsyncFn { f })),
        }
    }
}

impl<Fut, F> Clone for AndThenAsync<Fut, F>
where
    Fut: Future + Clone,
    Fut::Output: Try,
    F: FnMut<(<Fut::Output as Try>::Output,)> + Clone,
    F::Output: IntoFuture,
    <F::Output as IntoFuture>::Output: FromResidual<<Fut::Output as Try>::Residual>,
    <F::Output as IntoFuture>::IntoFuture: Clone,
{
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
        }
    }
}

impl<Fut, F> Future for AndThenAsync<Fut, F>
where
    Fut: Future,
    Fut::Output: Try,
    F: FnMut<(<Fut::Output as Try>::Output,)>,
    F::Output: IntoFuture,
    <F::Output as IntoFuture>::Output: FromResidual<<Fut::Output as Try>::Residual>,
{
    type Output = <F::Output as IntoFuture>::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        self.project().inner.poll(cx)
    }
}

impl<Fut, F> FusedFuture for AndThenAsync<Fut, F>
where
    Fut: FusedFuture,
    Fut::Output: Try,
    F: FnMut<(<Fut::Output as Try>::Output,)>,
    F::Output: IntoFuture,
    <F::Output as IntoFuture>::Output: FromResidual<<Fut::Output as Try>::Residual>,
    <F::Output as IntoFuture>::IntoFuture: FusedFuture,
{
    fn is_terminated(&self) -> bool {
        self.inner.is_terminated()
    }
}

#[cfg(test)]
mod tests {
    use crate::future::future_ext::FutureExt;
    use futures_core::FusedFuture;
    use futures_util::future::{self, Ready, TryFutureExt};
    use std::mem;
    use std::num::NonZeroU32;

    fn ok_plus_3(value: u32) -> Ready<Result<u32, u32>> {
        future::ok(value + 3)
    }

    fn err_plus_3(value: u32) -> Ready<Result<u32, u32>> {
        future::err(value + 3)
    }

    #[tokio::test]
    async fn test_and_then_async() {
        assert_eq!(future::ok::<_, u32>(2).slim_and_then_async(ok_plus_3).await, Ok(5));
        assert_eq!(future::ok::<_, u32>(2).slim_and_then_async(err_plus_3).await, Err(5));
        assert_eq!(future::err::<_, u32>(2).slim_and_then_async(ok_plus_3).await, Err(2));
        assert_eq!(future::err::<_, u32>(2).slim_and_then_async(err_plus_3).await, Err(2));
    }

    #[tokio::test]
    async fn test_and_then_async_with_option() {
        assert_eq!(
            future::ready(Some(2))
                .slim_and_then_async(|x| future::ready(Some(x + 3)))
                .await,
            Some(5),
        );
    }

    #[tokio::test]
    async fn test_and_then_async_clone() {
        let future = future::ok::<u32, u32>(2).slim_and_then_async(ok_plus_3);
        let future_2 = future.clone();

        assert_eq!(future.await, Ok(5));
        assert_eq!(future_2.await, Ok(5));
    }

    #[tokio::test]
    async fn test_and_then_async_fused_future() {
        let mut future = future::ok::<u32, u32>(2).slim_and_then_async(ok_plus_3);

        assert!(!future.is_terminated());
        assert_eq!(future.by_ref().await, Ok(5));
        assert!(future.is_terminated());
    }

    #[tokio::test]
    async fn test_and_then_async_is_slim() {
        let make_base_future = || crate::future::ok_by_copy::<_, u32>(NonZeroU32::new(2).unwrap()).slim_map_ok(drop);
        let base_future = make_base_future();
        let future_1 = make_base_future().slim_and_then_async(crate::future::ok_by_copy::<_, u32>);
        let future_2 = make_base_future().and_then(crate::future::ok_by_copy);

        assert_eq!(mem::size_of_val(&base_future), mem::size_of_val(&future_1));
        assert!(mem::size_of_val(&future_1) < mem::size_of_val(&future_2));
        assert_eq!(base_future.await, Ok(()));
        assert_eq!(future_1.await, Ok(()));
        assert_eq!(future_2.await, Ok(()));
    }
}