1use std::{
4 fmt, mem,
5 pin::Pin,
6 task::{Poll, Waker},
7};
8
9enum UiTaskState<R> {
10 Pending {
11 future: Pin<Box<dyn Future<Output = R> + Send>>,
12 event_loop_waker: Waker,
13 #[cfg(debug_assertions)]
14 last_update: Option<zng_var::VarUpdateId>,
15 },
16 Ready(R),
17 Cancelled,
18}
19impl<R: fmt::Debug> fmt::Debug for UiTaskState<R> {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::Pending { .. } => write!(f, "Pending"),
23 Self::Ready(arg0) => f.debug_tuple("Ready").field(arg0).finish(),
24 Self::Cancelled => unreachable!(),
25 }
26 }
27}
28
29#[derive(Debug)]
37pub struct UiTask<R>(UiTaskState<R>);
38impl<R> UiTask<R> {
39 pub fn new_raw<F>(event_loop_waker: Waker, task: impl IntoFuture<IntoFuture = F>) -> Self
43 where
44 F: Future<Output = R> + Send + 'static,
45 {
46 UiTask(UiTaskState::Pending {
47 future: Box::pin(task.into_future()),
48 event_loop_waker,
49 #[cfg(debug_assertions)]
50 last_update: None,
51 })
52 }
53
54 pub fn update(&mut self) -> Option<&R> {
68 if let UiTaskState::Pending {
69 future,
70 event_loop_waker,
71 #[cfg(debug_assertions)]
72 last_update,
73 ..
74 } = &mut self.0
75 {
76 #[cfg(debug_assertions)]
77 {
78 let update = Some(zng_var::VARS.update_id());
79 if *last_update == update {
80 tracing::error!("UiTask::update called twice in the same update");
81 }
82 *last_update = update;
83 }
84
85 if let Poll::Ready(r) = future.as_mut().poll(&mut std::task::Context::from_waker(event_loop_waker)) {
86 self.0 = UiTaskState::Ready(r);
87 }
88 }
89
90 if let UiTaskState::Ready(r) = &self.0 { Some(r) } else { None }
91 }
92
93 pub fn is_ready(&self) -> bool {
97 matches!(&self.0, UiTaskState::Ready(_))
98 }
99
100 pub fn into_result(mut self) -> Result<R, Self> {
107 match mem::replace(&mut self.0, UiTaskState::Cancelled) {
108 UiTaskState::Ready(r) => Ok(r),
109 p @ UiTaskState::Pending { .. } => Err(Self(p)),
110 UiTaskState::Cancelled => unreachable!(),
111 }
112 }
113
114 pub fn cancel(mut self) {
116 self.0 = UiTaskState::Cancelled;
117 }
118}
119impl<R> Drop for UiTask<R> {
120 fn drop(&mut self) {
121 if let UiTaskState::Pending { .. } = &self.0 {
122 #[cfg(debug_assertions)]
123 {
124 tracing::warn!("pending UiTask<{}> dropped", std::any::type_name::<R>());
125 }
126 #[cfg(not(debug_assertions))]
127 {
128 tracing::warn!("pending UiTask dropped");
129 }
130 }
131 }
132}