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