use std::{
future::{Future, IntoFuture},
pin::Pin,
task::{Poll, Waker},
};
enum CoroutineState<T> {
Future(Pin<Box<dyn Future<Output = T> + 'static>>),
Finished,
}
#[allow(missing_debug_implementations)]
pub struct Coroutine<T = ()> {
waker: Waker,
state: CoroutineState<T>,
}
impl<T> Coroutine<T> {
pub fn new(fut: impl IntoFuture<Output = T> + 'static) -> Self {
Self {
waker: Waker::noop().clone(),
state: CoroutineState::Future(Box::pin(fut.into_future())),
}
}
pub fn poll(&mut self) -> Option<T> {
match &mut self.state {
CoroutineState::Future(fut) => {
let mut context = std::task::Context::from_waker(&self.waker);
match fut.as_mut().poll(&mut context) {
Poll::Ready(v) => {
self.state = CoroutineState::Finished;
Some(v)
}
Poll::Pending => None,
}
}
CoroutineState::Finished => None,
}
}
}
struct YieldOp(bool);
impl Future for YieldOp {
type Output = ();
fn poll(
mut self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if !self.0 {
self.0 = true;
return std::task::Poll::Pending;
}
std::task::Poll::Ready(())
}
}
pub fn yield_now() -> impl Future<Output = ()> {
YieldOp(false)
}