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#[derive(Debug, Default)]
53pub enum DataState<T, E: ErrorBounds = anyhow::Error> {
54 #[default]
56 None,
57 AwaitingResponse(Awaiting<T, E>), Present(T),
61 Failed(DataStateError<E>),
63}
64
65impl<T, E: ErrorBounds> DataState<T, E> {
66 #[cfg(feature = "egui")]
67 pub fn egui_start_task<F, R>(&mut self, ui: &mut egui::Ui, f: F) -> CanMakeProgress
69 where
70 F: FnOnce() -> R,
71 R: Into<Awaiting<T, E>>,
72 {
73 let result = self.start_task(f);
74 if result.is_able_to_make_progress() {
75 ui.spinner();
76 }
77 result
78 }
79
80 #[must_use]
83 pub fn start_task<F, R>(&mut self, f: F) -> CanMakeProgress
84 where
85 F: FnOnce() -> R,
86 R: Into<Awaiting<T, E>>,
87 {
88 if self.is_none() {
89 *self = Self::AwaitingResponse(f().into());
90 CanMakeProgress::AbleToMakeProgress
91 } else {
92 debug_assert!(
93 false,
94 "No known good reason this path should be hit other than logic error"
95 );
96 CanMakeProgress::UnableToMakeProgress
97 }
98 }
99
100 pub fn poll(&mut self) -> &mut Self {
104 if let Self::AwaitingResponse(rx) = self
105 && let Some(new_state) = Self::await_data(rx)
106 {
107 *self = new_state;
108 }
109 self
110 }
111
112 #[cfg(feature = "egui")]
113 pub fn egui_poll_mut(
120 &mut self,
121 ui: &mut egui::Ui,
122 error_btn_text: Option<&str>,
123 ) -> Option<&mut T> {
124 match self {
125 Self::None => {}
126 Self::AwaitingResponse(_) => {
127 ui.spinner();
128 self.poll();
129 }
130 Self::Present(data) => {
131 return Some(data);
132 }
133 Self::Failed(e) => {
134 ui.colored_label(ui.visuals().error_fg_color, e.to_string());
135 if ui
136 .button(error_btn_text.unwrap_or("Clear Error Status"))
137 .clicked()
138 {
139 *self = Self::default();
140 }
141 }
142 }
143 None
144 }
145
146 #[cfg(feature = "egui")]
147 pub fn egui_poll(&mut self, ui: &mut egui::Ui, error_btn_text: Option<&str>) -> Option<&T> {
149 self.egui_poll_mut(ui, error_btn_text).map(|x| &*x)
150 }
151
152 pub fn await_data(rx: &mut Awaiting<T, E>) -> Option<Self> {
155 Some(match rx.0.try_recv() {
156 Ok(recv_opt) => match recv_opt {
157 Some(outcome_result) => match outcome_result {
158 Ok(data) => Self::Present(data),
159 Err(err_msg) => {
160 warn!(?err_msg, "Error response received instead of the data");
161 Self::Failed(DataStateError::ErrorResponse(err_msg))
162 }
163 },
164 None => {
165 return None;
166 }
167 },
168 Err(e) => {
169 error!("Error receiving on channel. Sender dropped.");
170 Self::Failed(DataStateError::SenderDropped(e))
171 }
172 })
173 }
174
175 pub fn present(&self) -> Option<&T> {
180 if let Self::Present(data) = self {
181 Some(data)
182 } else {
183 None
184 }
185 }
186
187 pub fn present_mut(&mut self) -> Option<&mut T> {
193 if let Self::Present(data) = self {
194 Some(data)
195 } else {
196 None
197 }
198 }
199
200 #[must_use]
204 pub fn is_present(&self) -> bool {
205 matches!(self, Self::Present(..))
206 }
207
208 #[must_use]
212 pub fn is_none(&self) -> bool {
213 matches!(self, Self::None)
214 }
215
216 #[must_use]
220 pub fn is_awaiting_response(&self) -> bool {
221 matches!(self, Self::AwaitingResponse(..))
222 }
223}
224
225impl<T, E: ErrorBounds> AsRef<Self> for DataState<T, E> {
226 fn as_ref(&self) -> &Self {
227 self
228 }
229}
230
231impl<T, E: ErrorBounds> AsMut<Self> for DataState<T, E> {
232 fn as_mut(&mut self) -> &mut Self {
233 self
234 }
235}
236
237impl<E: ErrorBounds> From<E> for DataStateError<E> {
238 fn from(value: E) -> Self {
239 Self::FromE(value)
240 }
241}
242
243impl From<&str> for DataStateError<anyhow::Error> {
244 fn from(value: &str) -> Self {
245 value.to_owned().into()
246 }
247}
248
249impl From<String> for DataStateError<anyhow::Error> {
250 fn from(value: String) -> Self {
251 anyhow!(value).into()
252 }
253}
254
255impl CanMakeProgress {
256 #[must_use]
260 pub fn is_able_to_make_progress(&self) -> bool {
261 matches!(self, Self::AbleToMakeProgress)
262 }
263
264 #[must_use]
268 pub fn is_unable_to_make_progress(&self) -> bool {
269 matches!(self, Self::UnableToMakeProgress)
270 }
271}