tui_dispatch_core/
resource.rs1use serde::{Deserialize, Serialize};
46
47#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
55#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
56pub enum DataResource<T> {
57 #[default]
59 Empty,
60 Loading,
62 Loaded(T),
64 Failed(String),
66}
67
68impl<T> DataResource<T> {
69 pub fn is_empty(&self) -> bool {
71 matches!(self, Self::Empty)
72 }
73
74 pub fn is_loading(&self) -> bool {
76 matches!(self, Self::Loading)
77 }
78
79 pub fn is_loaded(&self) -> bool {
81 matches!(self, Self::Loaded(_))
82 }
83
84 pub fn is_failed(&self) -> bool {
86 matches!(self, Self::Failed(_))
87 }
88
89 pub fn data(&self) -> Option<&T> {
91 match self {
92 Self::Loaded(t) => Some(t),
93 _ => None,
94 }
95 }
96
97 pub fn data_mut(&mut self) -> Option<&mut T> {
99 match self {
100 Self::Loaded(t) => Some(t),
101 _ => None,
102 }
103 }
104
105 pub fn error(&self) -> Option<&str> {
107 match self {
108 Self::Failed(e) => Some(e),
109 _ => None,
110 }
111 }
112
113 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> DataResource<U> {
117 match self {
118 Self::Empty => DataResource::Empty,
119 Self::Loading => DataResource::Loading,
120 Self::Loaded(t) => DataResource::Loaded(f(t)),
121 Self::Failed(e) => DataResource::Failed(e),
122 }
123 }
124
125 pub fn map_ref<U>(&self, f: impl FnOnce(&T) -> U) -> DataResource<U> {
127 match self {
128 Self::Empty => DataResource::Empty,
129 Self::Loading => DataResource::Loading,
130 Self::Loaded(t) => DataResource::Loaded(f(t)),
131 Self::Failed(e) => DataResource::Failed(e.clone()),
132 }
133 }
134
135 pub fn and_then<U>(self, f: impl FnOnce(T) -> DataResource<U>) -> DataResource<U> {
139 match self {
140 Self::Empty => DataResource::Empty,
141 Self::Loading => DataResource::Loading,
142 Self::Loaded(t) => f(t),
143 Self::Failed(e) => DataResource::Failed(e),
144 }
145 }
146
147 pub fn unwrap_or(self, default: T) -> T {
149 match self {
150 Self::Loaded(t) => t,
151 _ => default,
152 }
153 }
154
155 pub fn unwrap_or_else(self, f: impl FnOnce() -> T) -> T {
157 match self {
158 Self::Loaded(t) => t,
159 _ => f(),
160 }
161 }
162
163 pub fn as_ref(&self) -> DataResource<&T> {
165 match self {
166 Self::Empty => DataResource::Empty,
167 Self::Loading => DataResource::Loading,
168 Self::Loaded(t) => DataResource::Loaded(t),
169 Self::Failed(e) => DataResource::Failed(e.clone()),
170 }
171 }
172
173 pub fn is_settled(&self) -> bool {
175 matches!(self, Self::Loaded(_) | Self::Failed(_))
176 }
177
178 pub fn is_pending(&self) -> bool {
180 matches!(self, Self::Empty | Self::Loading)
181 }
182
183 pub fn start_loading(&mut self) -> bool
194 where
195 T: Clone,
196 {
197 if self.is_loading() {
198 false
199 } else {
200 *self = Self::Loading;
201 true
202 }
203 }
204}
205
206impl<T: Clone> DataResource<T> {
207 pub fn cloned(&self) -> Option<T> {
209 self.data().cloned()
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_default_is_empty() {
219 let resource: DataResource<String> = DataResource::default();
220 assert!(resource.is_empty());
221 }
222
223 #[test]
224 fn test_state_checks() {
225 let empty: DataResource<i32> = DataResource::Empty;
226 let loading: DataResource<i32> = DataResource::Loading;
227 let loaded: DataResource<i32> = DataResource::Loaded(42);
228 let failed: DataResource<i32> = DataResource::Failed("oops".to_string());
229
230 assert!(empty.is_empty());
231 assert!(!empty.is_loading());
232 assert!(empty.is_pending());
233 assert!(!empty.is_settled());
234
235 assert!(!loading.is_empty());
236 assert!(loading.is_loading());
237 assert!(loading.is_pending());
238 assert!(!loading.is_settled());
239
240 assert!(!loaded.is_empty());
241 assert!(!loaded.is_loading());
242 assert!(loaded.is_loaded());
243 assert!(!loaded.is_pending());
244 assert!(loaded.is_settled());
245
246 assert!(!failed.is_empty());
247 assert!(failed.is_failed());
248 assert!(!failed.is_pending());
249 assert!(failed.is_settled());
250 }
251
252 #[test]
253 fn test_data_accessors() {
254 let loaded: DataResource<i32> = DataResource::Loaded(42);
255 let failed: DataResource<i32> = DataResource::Failed("error".to_string());
256
257 assert_eq!(loaded.data(), Some(&42));
258 assert_eq!(failed.data(), None);
259 assert_eq!(failed.error(), Some("error"));
260 assert_eq!(loaded.error(), None);
261 }
262
263 #[test]
264 fn test_map() {
265 let loaded: DataResource<i32> = DataResource::Loaded(21);
266 let doubled = loaded.map(|x| x * 2);
267 assert_eq!(doubled.data(), Some(&42));
268
269 let loading: DataResource<i32> = DataResource::Loading;
270 let still_loading: DataResource<i32> = loading.map(|x| x * 2);
271 assert!(still_loading.is_loading());
272 }
273
274 #[test]
275 fn test_and_then() {
276 let loaded: DataResource<i32> = DataResource::Loaded(42);
277 let chained = loaded.and_then(|x| DataResource::Loaded(x.to_string()));
278 assert_eq!(chained.data(), Some(&"42".to_string()));
279
280 let failed: DataResource<i32> = DataResource::Failed("err".to_string());
281 let still_failed: DataResource<String> =
282 failed.and_then(|x| DataResource::Loaded(x.to_string()));
283 assert!(still_failed.is_failed());
284 }
285
286 #[test]
287 fn test_unwrap_or() {
288 let loaded: DataResource<i32> = DataResource::Loaded(42);
289 let empty: DataResource<i32> = DataResource::Empty;
290
291 assert_eq!(loaded.unwrap_or(0), 42);
292 assert_eq!(empty.unwrap_or(0), 0);
293 }
294
295 #[test]
296 fn test_start_loading() {
297 let mut resource: DataResource<i32> = DataResource::Empty;
298 assert!(resource.start_loading());
299 assert!(resource.is_loading());
300
301 assert!(!resource.start_loading());
303 assert!(resource.is_loading());
304 }
305
306 #[test]
307 fn test_serialize_deserialize() {
308 let loaded: DataResource<i32> = DataResource::Loaded(42);
309 let json = serde_json::to_string(&loaded).unwrap();
310 let restored: DataResource<i32> = serde_json::from_str(&json).unwrap();
311 assert_eq!(loaded, restored);
312
313 let failed: DataResource<i32> = DataResource::Failed("oops".to_string());
314 let json = serde_json::to_string(&failed).unwrap();
315 let restored: DataResource<i32> = serde_json::from_str(&json).unwrap();
316 assert_eq!(failed, restored);
317 }
318}