reqwest_cross/
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("Request 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,
34 UnableToMakeProgress,
35}
36
37#[derive(Debug)]
39pub struct Awaiting<T, E: ErrorBounds>(pub oneshot::Receiver<Result<T, E>>);
40impl<T, E: ErrorBounds> From<oneshot::Receiver<Result<T, E>>> for Awaiting<T, E> {
41 fn from(value: oneshot::Receiver<Result<T, E>>) -> Self {
42 Self(value)
43 }
44}
45
46#[derive(Debug, Default)]
49pub enum DataState<T, E: ErrorBounds = anyhow::Error> {
50 #[default]
52 None,
53 AwaitingResponse(Awaiting<T, E>), Present(T),
57 Failed(DataStateError<E>),
59}
60
61impl<T, E: ErrorBounds> DataState<T, E> {
62 #[cfg(feature = "egui")]
63 pub fn egui_start_request<F, R>(&mut self, ui: &mut egui::Ui, fetch_fn: F) -> CanMakeProgress
65 where
66 F: FnOnce() -> R,
67 R: Into<Awaiting<T, E>>,
68 {
69 let result = self.start_request(fetch_fn);
70 if result.is_able_to_make_progress() {
71 ui.spinner();
72 }
73 result
74 }
75
76 #[must_use]
79 pub fn start_request<F, R>(&mut self, fetch_fn: F) -> CanMakeProgress
80 where
81 F: FnOnce() -> R,
82 R: Into<Awaiting<T, E>>,
83 {
84 if self.is_none() {
85 *self = DataState::AwaitingResponse(fetch_fn().into());
86 CanMakeProgress::AbleToMakeProgress
87 } else {
88 debug_assert!(
89 false,
90 "No known good reason this path should be hit other than logic error"
91 );
92 CanMakeProgress::UnableToMakeProgress
93 }
94 }
95
96 pub fn poll(&mut self) -> &mut Self {
100 if let DataState::AwaitingResponse(rx) = self {
101 if let Some(new_state) = Self::await_data(rx) {
102 *self = new_state;
103 }
104 }
105 self
106 }
107
108 #[cfg(feature = "egui")]
109 pub fn egui_poll_mut(
116 &mut self,
117 ui: &mut egui::Ui,
118 error_btn_text: Option<&str>,
119 ) -> Option<&mut T> {
120 match self {
121 DataState::None => {}
122 DataState::AwaitingResponse(_) => {
123 ui.spinner();
124 self.poll();
125 }
126 DataState::Present(data) => {
127 return Some(data);
128 }
129 DataState::Failed(e) => {
130 ui.colored_label(ui.visuals().error_fg_color, e.to_string());
131 if ui
132 .button(error_btn_text.unwrap_or("Clear Error Status"))
133 .clicked()
134 {
135 *self = DataState::default();
136 }
137 }
138 }
139 None
140 }
141
142 #[cfg(feature = "egui")]
143 pub fn egui_poll(&mut self, ui: &mut egui::Ui, error_btn_text: Option<&str>) -> Option<&T> {
145 self.egui_poll_mut(ui, error_btn_text).map(|x| &*x)
146 }
147
148 pub fn await_data(rx: &mut Awaiting<T, E>) -> Option<Self> {
151 Some(match rx.0.try_recv() {
152 Ok(recv_opt) => match recv_opt {
153 Some(outcome_result) => match outcome_result {
154 Ok(data) => DataState::Present(data),
155 Err(err_msg) => {
156 warn!(?err_msg, "Error response received instead of the data");
157 DataState::Failed(DataStateError::ErrorResponse(err_msg))
158 }
159 },
160 None => {
161 return None;
162 }
163 },
164 Err(e) => {
165 error!("Error receiving on channel. Sender dropped.");
166 DataState::Failed(DataStateError::SenderDropped(e))
167 }
168 })
169 }
170
171 pub fn present(&self) -> Option<&T> {
176 if let Self::Present(data) = self {
177 Some(data)
178 } else {
179 None
180 }
181 }
182
183 pub fn present_mut(&mut self) -> Option<&mut T> {
189 if let Self::Present(data) = self {
190 Some(data)
191 } else {
192 None
193 }
194 }
195
196 #[must_use]
200 pub fn is_present(&self) -> bool {
201 matches!(self, Self::Present(..))
202 }
203
204 #[must_use]
208 pub fn is_none(&self) -> bool {
209 matches!(self, Self::None)
210 }
211}
212
213impl<T, E: ErrorBounds> AsRef<DataState<T, E>> for DataState<T, E> {
214 fn as_ref(&self) -> &DataState<T, E> {
215 self
216 }
217}
218
219impl<T, E: ErrorBounds> AsMut<DataState<T, E>> for DataState<T, E> {
220 fn as_mut(&mut self) -> &mut DataState<T, E> {
221 self
222 }
223}
224
225impl<E: ErrorBounds> From<E> for DataStateError<E> {
226 fn from(value: E) -> Self {
227 Self::FromE(value)
228 }
229}
230
231impl From<&str> for DataStateError<anyhow::Error> {
232 fn from(value: &str) -> Self {
233 value.to_string().into()
234 }
235}
236
237impl From<String> for DataStateError<anyhow::Error> {
238 fn from(value: String) -> Self {
239 anyhow!(value).into()
240 }
241}
242
243impl CanMakeProgress {
244 #[must_use]
248 pub fn is_able_to_make_progress(&self) -> bool {
249 matches!(self, Self::AbleToMakeProgress)
250 }
251
252 #[must_use]
256 pub fn is_unable_to_make_progress(&self) -> bool {
257 matches!(self, Self::UnableToMakeProgress)
258 }
259}