main_loop_async/
data_state.rs1use anyhow::anyhow;
4use futures::channel::oneshot;
5use std::fmt::{Debug, Display};
6use thiserror::Error;
7use tracing::{error, warn};
8
9pub trait ErrorBounds: Display + Send + Sync + 'static + Debug {}
11impl<T: Display + Send + Sync + 'static + Debug> ErrorBounds for T {}
12
13#[derive(Error, Debug)]
14pub enum DataStateError<E: ErrorBounds> {
16 #[error("Task sender was dropped")]
18 SenderDropped(oneshot::Canceled),
19
20 #[error("Response received was an error: {0}")]
22 ErrorResponse(E),
23
24 #[error(transparent)]
26 FromE(E),
27}
28
29#[derive(Debug)]
30pub enum CanMakeProgress {
33 AbleToMakeProgress,
35
36 UnableToMakeProgress,
39}
40
41#[derive(Debug)]
43pub struct Awaiting<T, E: ErrorBounds>(pub oneshot::Receiver<Result<T, E>>);
44impl<T, E: ErrorBounds> From<oneshot::Receiver<Result<T, E>>> for Awaiting<T, E> {
45 fn from(value: oneshot::Receiver<Result<T, E>>) -> Self {
46 Self(value)
47 }
48}
49
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53#[derive(Debug, Default)]
54pub enum DataState<T, E: ErrorBounds = anyhow::Error> {
55 #[default]
57 None,
58
59 #[cfg_attr(feature = "serde", serde(skip))]
60 AwaitingResponse(Awaiting<T, E>), Present(T),
65
66 #[cfg_attr(feature = "serde", serde(skip))]
67 Failed(DataStateError<E>),
69}
70
71impl<T, E: ErrorBounds> DataState<T, E> {
72 #[cfg(feature = "egui")]
73 pub fn egui_start_task<F, R>(&mut self, ui: &mut egui::Ui, f: F) -> CanMakeProgress
78 where
79 F: FnOnce() -> R,
80 R: Into<Awaiting<T, E>>,
81 {
82 let result = self.start_task(f);
83 if result.is_able_to_make_progress() {
84 ui.spinner();
85 }
86 result
87 }
88
89 #[must_use]
94 pub fn start_task<F, R>(&mut self, f: F) -> CanMakeProgress
95 where
96 F: FnOnce() -> R,
97 R: Into<Awaiting<T, E>>,
98 {
99 if self.is_none() {
100 *self = Self::AwaitingResponse(f().into());
101 CanMakeProgress::AbleToMakeProgress
102 } else {
103 debug_assert!(
104 false,
105 "No known good reason this path should be hit other than logic error"
106 );
107 CanMakeProgress::UnableToMakeProgress
108 }
109 }
110
111 pub fn set_none(&mut self) -> &mut Self {
113 *self = Self::None;
114 self
115 }
116
117 pub fn poll(&mut self) -> &mut Self {
121 if let Self::AwaitingResponse(rx) = self
122 && let Some(new_state) = Self::await_data(rx)
123 {
124 *self = new_state;
125 }
126 self
127 }
128
129 pub fn poll_take(&mut self) -> Option<T> {
132 self.poll();
133 if self.is_present() {
134 let mut result = Self::None;
136 std::mem::swap(self, &mut result);
137 let non_present_state = match result {
138 Self::Present(inner) => return Some(inner),
139 Self::None => "None",
140 Self::AwaitingResponse(_) => "Awaiting",
141 Self::Failed(_) => "Error",
142 };
143 unreachable!(
144 "we checked that self.is_present() was true then we tried to extract the value but got: {non_present_state:?} State"
145 )
146 } else {
147 None
148 }
149 }
150
151 #[cfg(feature = "egui")]
152 pub fn egui_poll_mut(
159 &mut self,
160 ui: &mut egui::Ui,
161 error_btn_text: Option<&str>,
162 ) -> Option<&mut T> {
163 match self {
164 Self::None => {}
165 Self::AwaitingResponse(_) => {
166 ui.spinner();
167 self.poll();
168 }
169 Self::Present(data) => {
170 return Some(data);
171 }
172 Self::Failed(e) => {
173 ui.colored_label(ui.visuals().error_fg_color, e.to_string());
174 if ui
175 .button(error_btn_text.unwrap_or("Clear Error Status"))
176 .clicked()
177 {
178 *self = Self::default();
179 }
180 }
181 }
182 None
183 }
184
185 #[cfg(feature = "egui")]
186 pub fn egui_poll(&mut self, ui: &mut egui::Ui, error_btn_text: Option<&str>) -> Option<&T> {
188 self.egui_poll_mut(ui, error_btn_text).map(|x| &*x)
189 }
190
191 #[cfg(feature = "egui")]
192 #[must_use]
196 pub fn egui_poll_take(&mut self, ui: &mut egui::Ui, error_btn_text: Option<&str>) -> Option<T> {
197 self.egui_poll_mut(ui, error_btn_text);
198 self.poll_take()
199 }
200
201 pub fn await_data(rx: &mut Awaiting<T, E>) -> Option<Self> {
204 Some(match rx.0.try_recv() {
205 Ok(recv_opt) => match recv_opt {
206 Some(outcome_result) => match outcome_result {
207 Ok(data) => Self::Present(data),
208 Err(err_msg) => {
209 warn!(?err_msg, "Error response received instead of the data");
210 Self::Failed(DataStateError::ErrorResponse(err_msg))
211 }
212 },
213 None => {
214 return None;
215 }
216 },
217 Err(e) => {
218 error!("Error receiving on channel. Sender dropped.");
219 Self::Failed(DataStateError::SenderDropped(e))
220 }
221 })
222 }
223
224 pub fn present(&self) -> Option<&T> {
229 if let Self::Present(data) = self {
230 Some(data)
231 } else {
232 None
233 }
234 }
235
236 pub fn present_mut(&mut self) -> Option<&mut T> {
242 if let Self::Present(data) = self {
243 Some(data)
244 } else {
245 None
246 }
247 }
248
249 #[must_use]
253 pub fn is_present(&self) -> bool {
254 matches!(self, Self::Present(..))
255 }
256
257 #[must_use]
261 pub fn is_none(&self) -> bool {
262 matches!(self, Self::None)
263 }
264
265 #[must_use]
269 pub fn is_awaiting_response(&self) -> bool {
270 matches!(self, Self::AwaitingResponse(..))
271 }
272}
273
274impl<T, E: ErrorBounds> AsRef<Self> for DataState<T, E> {
275 fn as_ref(&self) -> &Self {
276 self
277 }
278}
279
280impl<T, E: ErrorBounds> AsMut<Self> for DataState<T, E> {
281 fn as_mut(&mut self) -> &mut Self {
282 self
283 }
284}
285
286impl<E: ErrorBounds> From<E> for DataStateError<E> {
287 fn from(value: E) -> Self {
288 Self::FromE(value)
289 }
290}
291
292impl From<&str> for DataStateError<anyhow::Error> {
293 fn from(value: &str) -> Self {
294 value.to_owned().into()
295 }
296}
297
298impl From<String> for DataStateError<anyhow::Error> {
299 fn from(value: String) -> Self {
300 anyhow!(value).into()
301 }
302}
303
304impl CanMakeProgress {
305 #[must_use]
309 pub fn is_able_to_make_progress(&self) -> bool {
310 matches!(self, Self::AbleToMakeProgress)
311 }
312
313 #[must_use]
317 pub fn is_unable_to_make_progress(&self) -> bool {
318 matches!(self, Self::UnableToMakeProgress)
319 }
320}