use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::boxed::Box;
use std::sync::Arc;
use concurrent_queue::ConcurrentQueue;
use crate::valtron::{
queue_waker, Ctx, QueueReadiness, TaskIterator, TaskStatus, WakeToken,
};
pub type TransitionOutput<T, R, P> = Option<(T, TaskStatus<R, P>)>;
pub struct StateTask<T, R, P, F> {
state: Option<T>,
transition: F,
completed: bool,
_marker: PhantomData<fn() -> (R, P)>,
}
impl<T, R, P, F> StateTask<T, R, P, F> {
pub fn new(state: T, transition: F) -> Self {
Self {
state: Some(state),
transition,
completed: false,
_marker: PhantomData,
}
}
}
impl<T, R, P, F> TaskIterator for StateTask<T, R, P, F>
where
F: FnMut(Ctx, T) -> Option<(T, TaskStatus<R, P>)>,
{
type Ready = R;
type Pending = P;
fn next_status(&mut self, ctx: Ctx) -> Option<TaskStatus<Self::Ready, Self::Pending>> {
if self.completed {
return None;
}
let state = self.state.take()?;
match (self.transition)(ctx, state) {
Some((next, status)) => {
self.state = Some(next);
Some(status)
}
None => {
self.completed = true;
None
}
}
}
}
#[cfg(feature = "multi")]
pub type AsyncTransition<T, R, P> = Box<
dyn Fn(Ctx, T) -> Pin<Box<dyn Future<Output = TransitionOutput<T, R, P>> + Send>> + Send,
>;
#[cfg(not(feature = "multi"))]
pub type AsyncTransition<T, R, P> =
Box<dyn Fn(Ctx, T) -> Pin<Box<dyn Future<Output = TransitionOutput<T, R, P>>>>>;
#[cfg(feature = "multi")]
pub type AsyncStateFuture<T, R, P> =
Pin<Box<dyn Future<Output = TransitionOutput<T, R, P>> + Send>>;
#[cfg(not(feature = "multi"))]
pub type AsyncStateFuture<T, R, P> =
Pin<Box<dyn Future<Output = TransitionOutput<T, R, P>>>>;
pub struct AsyncStateTask<T, R, P>
where
T: 'static,
R: 'static,
P: 'static,
{
state: Option<T>,
transition: AsyncTransition<T, R, P>,
future: Option<AsyncStateFuture<T, R, P>>,
wake_queue: Arc<ConcurrentQueue<WakeToken>>,
completed: bool,
}
impl<T, R, P> AsyncStateTask<T, R, P>
where
T: 'static,
R: 'static,
P: 'static,
{
pub fn new(state: T, transition: AsyncTransition<T, R, P>) -> Self {
Self {
state: Some(state),
transition,
future: None,
wake_queue: Arc::new(ConcurrentQueue::unbounded()),
completed: false,
}
}
}
#[cfg(feature = "multi")]
impl<T, R, P> TaskIterator for AsyncStateTask<T, R, P>
where
T: Send + 'static,
R: Send + 'static,
P: Send + 'static,
{
type Ready = R;
type Pending = P;
fn next_status(&mut self, ctx: Ctx) -> Option<TaskStatus<Self::Ready, Self::Pending>> {
if self.completed {
return None;
}
if self.future.is_none() {
let state = self.state.take()?;
self.future = Some((self.transition)(ctx, state));
}
while self.wake_queue.pop().is_ok() {}
let waker = queue_waker(self.wake_queue.clone(), 0);
let mut cx = Context::from_waker(&waker);
match self.future.as_mut().unwrap().as_mut().poll(&mut cx) {
Poll::Ready(Some((next, status))) => {
self.state = Some(next);
self.future = None;
Some(status)
}
Poll::Ready(None) => {
self.completed = true;
None
}
Poll::Pending => {
if self.wake_queue.is_empty() {
Some(TaskStatus::Depends(Arc::new(QueueReadiness::new(
self.wake_queue.clone(),
))))
} else {
Some(TaskStatus::Ignore)
}
}
}
}
}
#[cfg(not(feature = "multi"))]
impl<T, R, P> TaskIterator for AsyncStateTask<T, R, P>
where
T: 'static,
R: 'static,
P: 'static,
{
type Ready = R;
type Pending = P;
fn next_status(&mut self, ctx: Ctx) -> Option<TaskStatus<Self::Ready, Self::Pending>> {
if self.completed {
return None;
}
if self.future.is_none() {
let state = self.state.take()?;
self.future = Some((self.transition)(ctx, state));
}
while self.wake_queue.pop().is_ok() {}
let waker = queue_waker(self.wake_queue.clone(), 0);
let mut cx = Context::from_waker(&waker);
match self.future.as_mut().unwrap().as_mut().poll(&mut cx) {
Poll::Ready(Some((next, status))) => {
self.state = Some(next);
self.future = None;
Some(status)
}
Poll::Ready(None) => {
self.completed = true;
None
}
Poll::Pending => {
if self.wake_queue.is_empty() {
Some(TaskStatus::Depends(Arc::new(QueueReadiness::new(
self.wake_queue.clone(),
))))
} else {
Some(TaskStatus::Ignore)
}
}
}
}
}