atask 0.1.1

An async task implementation
Documentation
#![expect(clippy::redundant_async_block)]

use crate::{Builder, JoinHandle, Result, Runnable};

use std::{
    collections::VecDeque,
    pin::pin,
    sync::{Arc, Mutex},
    task::{Context, Poll, Waker},
};

fn poll_loop<R>(future: impl Future<Output = R>) -> R {
    let mut future = pin!(future);
    let waker = Waker::noop();
    let mut cx = Context::from_waker(waker);
    loop {
        if let Poll::Ready(value) = future.as_mut().poll(&mut cx) {
            return value;
        }
    }
}

struct Runtime {
    queue: Arc<Mutex<VecDeque<Runnable>>>,
}

impl Runtime {
    fn new() -> Runtime {
        Runtime {
            queue: Arc::new(Mutex::new(VecDeque::new())),
        }
    }

    fn spawn<R: Send + 'static>(
        &self,
        task: impl Future<Output = R> + Send + 'static,
    ) -> JoinHandle<R> {
        let queue = Arc::clone(&self.queue);
        let (runnable, handle) = Builder::new()
            .catch_unwind(true)
            .spawn(task, move |r| queue.lock().unwrap().push_back(r));
        runnable.schedule();
        handle
    }

    fn spawn_local<R: 'static>(&self, task: impl Future<Output = R> + 'static) -> JoinHandle<R> {
        let queue = Arc::clone(&self.queue);
        let (runnable, handle) = Builder::new()
            .catch_unwind(true)
            .spawn_local(task, move |r| queue.lock().unwrap().push_back(r));
        runnable.schedule();
        handle
    }

    fn run<R: Send + 'static>(
        &self,
        future: impl Future<Output = R> + Send + 'static,
    ) -> Result<R> {
        let handle = self.spawn(future);
        while !handle.finished() {
            self.queue.lock().unwrap().pop_front().unwrap().run();
        }
        poll_loop(handle)
    }
}

#[test]
fn run_block() -> Result<()> {
    let rt = Runtime::new();
    let res = rt.run(async { "hewwo" })?;
    assert_eq!(res, "hewwo");
    Ok(())
}

#[test]
fn await_task() -> Result<()> {
    let rt = Runtime::new();
    let handle = rt.spawn(async { "hewwo" });
    let res = rt.run(async move { handle.await })??;
    assert_eq!(res, "hewwo");
    Ok(())
}

#[test]
fn local_future() -> Result<()> {
    let rt = Runtime::new();
    let handle = rt.spawn_local(async { "hewwo" });
    let res = rt.run(async move { handle.await })??;
    assert_eq!(res, "hewwo");
    Ok(())
}