slim-futures 0.1.0-alpha.0

Asynchronous tools that intends for smaller binary size.
Documentation
use crate::support;
use core::future::{Future, IntoFuture};
use core::pin::Pin;
use core::task::{Context, Poll};
use futures_core::FusedFuture;

pin_project_lite::pin_project! {
    pub struct RawSelect<Fut1, Fut2> {
        #[pin]
        fut_1: Fut1,
        #[pin]
        fut_2: Fut2,
    }
}

impl<Fut1, Fut2> RawSelect<Fut1, Fut2> {
    pub(crate) fn new(fut_1: Fut1, fut_2: Fut2) -> Self {
        Self { fut_1, fut_2 }
    }
}

impl<Fut1, Fut2> Clone for RawSelect<Fut1, Fut2>
where
    Fut1: Clone,
    Fut2: Clone,
{
    fn clone(&self) -> Self {
        Self {
            fut_1: self.fut_1.clone(),
            fut_2: self.fut_2.clone(),
        }
    }
}

impl<Fut1, Fut2> Future for RawSelect<Fut1, Fut2>
where
    Fut1: Future,
    Fut2: Future<Output = Fut1::Output>,
{
    type Output = Fut1::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        let this = self.project();

        match this.fut_1.poll(cx) {
            Poll::Ready(result) => Poll::Ready(result),
            Poll::Pending => this.fut_2.poll(cx),
        }
    }
}

impl<Fut1, Fut2> FusedFuture for RawSelect<Fut1, Fut2>
where
    Fut1: FusedFuture,
    Fut2: FusedFuture<Output = Fut1::Output>,
{
    fn is_terminated(&self) -> bool {
        self.fut_1.is_terminated() || self.fut_2.is_terminated()
    }
}

pub fn raw_select<Fut1, Fut2>(fut_1: Fut1, fut_2: Fut2) -> RawSelect<Fut1::IntoFuture, Fut2::IntoFuture>
where
    Fut1: IntoFuture,
    Fut2: IntoFuture<Output = Fut1::Output>,
{
    support::assert_future::<_, Fut1::Output>(RawSelect::new(fut_1.into_future(), fut_2.into_future()))
}

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

    #[tokio::test]
    async fn test_raw_select() {
        assert_eq!(
            future::raw_select(future::ready_by_copy(2), future::ready_by_copy(3)).await,
            2
        );

        assert_eq!(
            future::raw_select(
                test_utilities::delayed(future::ready_by_copy(2)),
                future::ready_by_copy(3)
            )
            .await,
            3,
        );
    }

    #[tokio::test]
    async fn test_raw_select_clone() {
        let future = future::raw_select(future::ready_by_copy(2), future::ready_by_copy(3));
        let future_2 = future.clone();

        assert_eq!(future.await, 2);
        assert_eq!(future_2.await, 2);
    }

    #[tokio::test]
    async fn test_raw_select_fused_future() {
        let pending = || futures_util::future::ready(());

        let terminated = || {
            let mut result = pending();

            (&mut result).now_or_never().unwrap();

            result
        };

        assert!(!future::raw_select(pending(), pending()).is_terminated());
        assert!(future::raw_select(pending(), terminated()).is_terminated());
        assert!(future::raw_select(terminated(), pending()).is_terminated());
        assert!(future::raw_select(terminated(), terminated()).is_terminated());
    }

    #[tokio::test]
    async fn test_raw_select_is_slim() {
        let make_base_future_1 = || future::lazy(|_| 2);
        let make_base_future_2 = || future::ready_by_copy(NonZeroU32::new(3).unwrap()).slim_map(NonZeroU32::get);
        let base_future_1 = make_base_future_1();
        let base_future_2 = make_base_future_2();
        let future = future::raw_select(make_base_future_1(), make_base_future_2());

        assert_eq!(mem::size_of_val(&base_future_2), mem::size_of_val(&future));
        assert_eq!(base_future_1.await, 2);
        assert_eq!(base_future_2.await, 3);
        assert_eq!(future.await, 2);
    }
}