completion 0.2.1

Utilities for writing completion-based asynchronous code
Documentation
#![deny(warnings)]
#![deny(
    absolute_paths_not_starting_with_crate,
    box_pointers,
    explicit_outlives_requirements,
    keyword_idents,
    macro_use_extern_crate,
    meta_variable_misuse,
    missing_copy_implementations,
    missing_crate_level_docs,
    missing_debug_implementations,
    missing_doc_code_examples,
    missing_docs,
    pointer_structural_match,
    private_doc_tests,
    single_use_lifetimes,
    trivial_casts,
    trivial_numeric_casts,
    unaligned_references,
    unreachable_pub,
    unsafe_code,
    unstable_features,
    unused_extern_crates,
    unused_import_braces,
    unused_lifetimes,
    unused_qualifications,
    unused_results,
    variant_size_differences
)]
#![deny(clippy::pedantic)]

use std::borrow::Cow;
use std::cell::Cell;
use std::future::{self, Future};
use std::pin::Pin;
use std::task::{Context, Poll};

use completion_core::CompletionFuture;
use futures_lite::{future::yield_now, pin};

use crate::test_utils;
use crate::CompletionFutureExt;
use crate::{completion, completion_async, completion_async_move, future::block_on};

#[completion(crate = crate)]
async fn empty_async() {}

#[test]
fn empty() {
    block_on(empty_async());
}

#[completion(crate = crate)]
async fn nested_async() {
    #[completion(crate = crate)]
    async fn inner_function() {
        yield_now().await;
        empty_async().await;
        yield_now().await;
    }
    async fn inner_function2() {
        yield_now().await;
        yield_now().await;
        yield_now().await;
    }

    empty_async().await;
    inner_function().await;
    inner_function2().await;
    inner_function().await;
}

#[test]
fn nested() {
    block_on(nested_async());
}

#[completion(crate = crate)]
#[allow(single_use_lifetimes, clippy::trivially_copy_pass_by_ref)]
async fn lifetimes_async<'a, 'b, T>(x: &'a T, y: &&&String, z: &mut Cow<'a, str>) -> &'a T {
    let _ = y;
    let _ = z;
    x
}

#[test]
fn lifetimes() {
    let _: &i32 = block_on(lifetimes_async(
        &5,
        &&&String::new(),
        &mut Cow::Borrowed(""),
    ));
}

struct X;
impl X {
    #[completion(crate = crate)]
    #[allow(clippy::trivially_copy_pass_by_ref)]
    async fn method(&self, param: &'static i32) -> impl Clone {
        let _ = self;
        let _ = param;
        5
    }
}

#[test]
fn method() {
    let x = block_on(X.method(&9));
    drop(x.clone());
}

#[test]
fn block_yielding() {
    assert_eq!(
        block_on(completion_async! {
            yield_now().await;
            yield_now().await;
            5
        }),
        5
    );
}

#[test]
fn block_move() {
    fn block_on_static<F: CompletionFuture + 'static>(fut: F) -> F::Output {
        block_on(fut)
    }

    let data = "abc".to_owned();

    assert_eq!(block_on(completion_async! { data.clone() }), "abc");
    assert_eq!(
        block_on_static(completion_async_move! { data.clone() }),
        "abc"
    );
}

#[test]
fn block_try() {
    let res = block_on(completion_async! {
        let res = Ok::<_, i8>(5);
        assert_eq!(res?, 5);
        let res = Err(6);
        res?;
        unreachable!();

        #[allow(unreachable_code)]
        Ok(())
    });
    assert_eq!(res, Err(6));
}

#[test]
fn send() {
    fn requires_send<T: Send>(_: T) {}
    #[completion(crate = crate)]
    async fn x() {}

    requires_send(completion_async! {});
    requires_send(completion_async_move! {
        yield_now().await;
    });
    requires_send(x());
}

#[test]
fn cancel_completion_future() {
    struct Fut<'a> {
        stage: u8,
        number: &'a Cell<i32>,
    }
    #[allow(unsafe_code)]
    impl CompletionFuture for Fut<'_> {
        type Output = ();
        unsafe fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            self.number.set(1);
            cx.waker().wake_by_ref();
            Poll::Pending
        }
        unsafe fn poll_cancel(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
            match self.stage {
                0 => self.number.set(2),
                1 => {
                    self.number.set(3);
                    return Poll::Ready(());
                }
                _ => unreachable!(),
            }
            self.stage += 1;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }

    let number = Cell::new(0);

    let fut = completion_async! {
        Fut { stage: 0, number: &number }.await;
    };
    futures_lite::pin!(fut);

    assert_eq!(test_utils::poll_once(fut.as_mut()), None);
    assert_eq!(number.get(), 1);

    assert_eq!(test_utils::poll_cancel_once(fut.as_mut()), false);
    assert_eq!(number.get(), 2);

    assert_eq!(test_utils::poll_cancel_once(fut.as_mut()), true);
    assert_eq!(number.get(), 3);
}

#[test]
fn cancel_future() {
    struct Fut<'a> {
        number: &'a Cell<i32>,
    }
    #[allow(unsafe_code)]
    impl CompletionFuture for Fut<'_> {
        type Output = ();
        unsafe fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
            panic!()
        }
        unsafe fn poll_cancel(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
            panic!()
        }
    }
    impl Future for Fut<'_> {
        type Output = ();
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            self.number.set(self.number.get() + 1);
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }

    let number = Cell::new(0);

    let fut = completion_async! {
        number.set(1000);
        let _ = future::ready(5).must_complete().await;
        number.set(1);
        Fut { number: &number }.await;
    };
    pin!(fut);

    assert_eq!(test_utils::poll_once(fut.as_mut()), None);
    assert_eq!(number.get(), 2);

    assert_eq!(test_utils::poll_cancel_once(fut.as_mut()), true);
    assert_eq!(number.get(), 2);
}