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 Self::new_raw_boxed(event_loop_waker, Box::pin(task.into_future()))
47 }
48
49 pub fn new_raw_boxed(event_loop_waker: Waker, task: Pin<Box<dyn Future<Output = R> + Send + 'static>>) -> Self {
51 UiTask(UiTaskState::Pending {
52 future: task,
53 event_loop_waker,
54 #[cfg(debug_assertions)]
55 last_update: None,
56 })
57 }
58
59 pub fn update(&mut self) -> Option<&R> {
73 if let UiTaskState::Pending {
74 future,
75 event_loop_waker,
76 #[cfg(debug_assertions)]
77 last_update,
78 ..
79 } = &mut self.0
80 {
81 #[cfg(debug_assertions)]
82 {
83 let update = Some(zng_var::VARS.update_id());
84 if *last_update == update {
85 tracing::error!("UiTask::update called twice in the same update");
86 }
87 *last_update = update;
88 }
89
90 if let Poll::Ready(r) = future.as_mut().poll(&mut std::task::Context::from_waker(event_loop_waker)) {
91 self.0 = UiTaskState::Ready(r);
92 }
93 }
94
95 if let UiTaskState::Ready(r) = &self.0 { Some(r) } else { None }
96 }
97
98 pub fn is_ready(&self) -> bool {
102 matches!(&self.0, UiTaskState::Ready(_))
103 }
104
105 pub fn into_result(mut self) -> Result<R, Self> {
112 match mem::replace(&mut self.0, UiTaskState::Cancelled) {
113 UiTaskState::Ready(r) => Ok(r),
114 p @ UiTaskState::Pending { .. } => Err(Self(p)),
115 UiTaskState::Cancelled => unreachable!(),
116 }
117 }
118
119 pub fn cancel(mut self) {
121 self.0 = UiTaskState::Cancelled;
122 }
123}
124impl<R: Send + 'static> IntoFuture for UiTask<R> {
125 type Output = R;
126
127 type IntoFuture = Pin<Box<dyn Future<Output = R> + Send>>;
128
129 fn into_future(mut self) -> Self::IntoFuture {
130 match mem::replace(&mut self.0, UiTaskState::Cancelled) {
131 UiTaskState::Pending { future, .. } => future,
132 UiTaskState::Ready(r) => Box::pin(async move { r }),
133 UiTaskState::Cancelled => unreachable!(),
134 }
135 }
136}
137impl<R> Drop for UiTask<R> {
138 fn drop(&mut self) {
139 if let UiTaskState::Pending { .. } = &self.0 {
140 #[cfg(debug_assertions)]
141 {
142 tracing::warn!("pending UiTask<{}> dropped", std::any::type_name::<R>());
143 }
144 #[cfg(not(debug_assertions))]
145 {
146 tracing::warn!("pending UiTask dropped");
147 }
148 }
149 }
150}