use ferroui_base::reactive::IDisposable;
use ferroui_base::threading::{Dispatcher, DispatcherPriority, DispatcherTask, DispatcherTimer, OperationCanceledError};
use std::cell::RefCell;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use std::time::Duration;
pub struct TaskHandle<T: 'static> {
task: DispatcherTask<T>,
identity: Rc<()>,
}
pub type ActionTask = TaskHandle<()>;
impl<T: 'static> TaskHandle<T> {
pub fn spawn<Fut>(future: Fut) -> Self
where
Fut: Future<Output = T> + 'static,
{
Self::from_task(Dispatcher::ui_thread().invoke_async_task_local(move || future))
}
pub fn from_task(task: DispatcherTask<T>) -> Self {
Self { task, identity: Rc::new(()) }
}
pub fn task(&self) -> &DispatcherTask<T> {
&self.task
}
pub fn is_completed(&self) -> bool {
self.task.is_completed()
}
pub fn is_completed_successfully(&self) -> bool {
self.task.is_completed_successfully()
}
pub fn is_faulted(&self) -> bool {
self.task.is_faulted()
}
pub fn is_canceled(&self) -> bool {
self.task.is_canceled()
}
}
impl ActionTask {
pub fn completed() -> Self {
Self::spawn(std::future::ready(()))
}
}
impl<T: 'static> Clone for TaskHandle<T> {
fn clone(&self) -> Self {
Self { task: self.task.clone(), identity: self.identity.clone() }
}
}
impl<T: 'static> PartialEq for TaskHandle<T> {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.identity, &other.identity)
}
}
impl<T: 'static> fmt::Debug for TaskHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TaskHandle({:?})", self.task)
}
}
impl<T: 'static> Future for TaskHandle<T> {
type Output = Result<T, OperationCanceledError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.get_mut().task).poll(cx)
}
}
#[derive(Default)]
struct DelayState {
elapsed: bool,
waker: Option<Waker>,
timer: Option<Rc<dyn IDisposable>>,
}
pub struct Delay {
duration: Duration,
state: Rc<RefCell<DelayState>>,
}
pub fn delay(duration: Duration) -> Delay {
Delay { duration, state: Rc::new(RefCell::new(DelayState::default())) }
}
impl Future for Delay {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let this = self.get_mut();
if this.duration.is_zero() {
return Poll::Ready(());
}
let mut state = this.state.borrow_mut();
if state.elapsed {
return Poll::Ready(());
}
state.waker = Some(cx.waker().clone());
if state.timer.is_none() {
let shared = Rc::downgrade(&this.state);
state.timer = Some(DispatcherTimer::run_once(
move || {
let Some(shared) = shared.upgrade() else {
return;
};
let waker = {
let mut state = shared.borrow_mut();
state.elapsed = true;
state.waker.take()
};
if let Some(waker) = waker {
waker.wake();
}
},
this.duration,
DispatcherPriority::BACKGROUND,
));
}
Poll::Pending
}
}
impl Drop for Delay {
fn drop(&mut self) {
let timer = self.state.borrow_mut().timer.take();
if let Some(timer) = timer {
timer.dispose();
}
}
}
pub(crate) struct CatchUnwind<F> {
future: Pin<Box<F>>,
}
impl<F: Future> CatchUnwind<F> {
pub(crate) fn new(future: F) -> Self {
Self { future: Box::pin(future) }
}
}
impl<F: Future> Future for CatchUnwind<F> {
type Output = Option<F::Output>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| this.future.as_mut().poll(cx))) {
Ok(Poll::Ready(output)) => Poll::Ready(Some(output)),
Ok(Poll::Pending) => Poll::Pending,
Err(_) => Poll::Ready(None),
}
}
}