1#![allow(missing_docs)]
2use std::sync::{Arc, Mutex};
24
25pub struct LazyRef<T> {
29 value: Arc<Mutex<Option<T>>>,
30 loader: Arc<dyn Fn() -> T + Send + Sync>,
31}
32
33impl<T: Clone + Send + Sync + 'static> LazyRef<T> {
34 pub fn new<F>(loader: F) -> Self
36 where
37 F: Fn() -> T + Send + Sync + 'static,
38 {
39 Self {
40 value: Arc::new(Mutex::new(None)),
41 loader: Arc::new(loader),
42 }
43 }
44
45 pub fn loaded(value: T) -> Self {
47 Self {
48 value: Arc::new(Mutex::new(Some(value))),
49 loader: Arc::new(|| panic!("LazyRef::loaded should not call loader")),
50 }
51 }
52
53 pub fn get(&self) -> Option<T> {
55 let mut guard = self.value.lock().unwrap();
56 if guard.is_none() {
57 *guard = Some((self.loader)());
58 }
59 guard.clone()
60 }
61
62 pub fn is_loaded(&self) -> bool {
64 self.value.lock().unwrap().is_some()
65 }
66
67 pub fn reload(&self) -> Option<T> {
69 let mut guard = self.value.lock().unwrap();
70 *guard = Some((self.loader)());
71 guard.clone()
72 }
73
74 pub fn invalidate(&self) {
76 *self.value.lock().unwrap() = None;
77 }
78}
79
80impl<T: Clone + Send + Sync + 'static> Clone for LazyRef<T> {
81 fn clone(&self) -> Self {
82 Self {
83 value: Arc::clone(&self.value),
84 loader: Arc::clone(&self.loader),
85 }
86 }
87}
88
89pub struct LazyCollection<T> {
93 inner: LazyRef<Vec<T>>,
94}
95
96impl<T: Clone + Send + Sync + 'static> LazyCollection<T> {
97 pub fn new<F>(loader: F) -> Self
98 where
99 F: Fn() -> Vec<T> + Send + Sync + 'static,
100 {
101 Self {
102 inner: LazyRef::new(loader),
103 }
104 }
105
106 pub fn loaded(items: Vec<T>) -> Self {
107 Self {
108 inner: LazyRef::loaded(items),
109 }
110 }
111
112 pub fn all(&self) -> Vec<T> {
114 self.inner.get().unwrap_or_default()
115 }
116
117 pub fn len(&self) -> usize {
119 self.all().len()
120 }
121
122 pub fn is_empty(&self) -> bool {
124 self.len() == 0
125 }
126
127 pub fn is_loaded(&self) -> bool {
129 self.inner.is_loaded()
130 }
131
132 pub fn filter<F>(&self, predicate: F) -> Vec<T>
134 where
135 F: Fn(&T) -> bool,
136 {
137 self.all().into_iter().filter(predicate).collect()
138 }
139
140 pub fn invalidate(&self) {
142 self.inner.invalidate();
143 }
144}
145
146impl<T: Clone + Send + Sync + 'static> Clone for LazyCollection<T> {
147 fn clone(&self) -> Self {
148 Self {
149 inner: self.inner.clone(),
150 }
151 }
152}
153
154pub struct LazyLoader {
158 load_count: Arc<Mutex<usize>>,
159}
160
161impl Default for LazyLoader {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167impl LazyLoader {
168 pub fn new() -> Self {
169 Self {
170 load_count: Arc::new(Mutex::new(0)),
171 }
172 }
173
174 pub fn lazy<F, T>(&self, loader: F) -> LazyRef<T>
176 where
177 T: Clone + Send + Sync + 'static,
178 F: Fn() -> T + Send + Sync + 'static,
179 {
180 let count = Arc::clone(&self.load_count);
181 LazyRef::new(move || {
182 *count.lock().unwrap() += 1;
183 loader()
184 })
185 }
186
187 pub fn load_count(&self) -> usize {
189 *self.load_count.lock().unwrap()
190 }
191
192 pub fn reset_count(&self) {
194 *self.load_count.lock().unwrap() = 0;
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_lazy_ref_first_access_loads() {
204 let lazy: LazyRef<String> = LazyRef::new(|| "hello".to_string());
205 assert!(!lazy.is_loaded());
206 let val = lazy.get().unwrap();
207 assert_eq!(val, "hello");
208 assert!(lazy.is_loaded());
209 }
210
211 #[test]
212 fn test_lazy_ref_cached_after_load() {
213 let counter = Arc::new(Mutex::new(0));
214 let c = Arc::clone(&counter);
215 let lazy: LazyRef<i32> = LazyRef::new(move || {
216 *c.lock().unwrap() += 1;
217 42
218 });
219
220 assert_eq!(lazy.get().unwrap(), 42);
221 assert_eq!(lazy.get().unwrap(), 42);
222 assert_eq!(lazy.get().unwrap(), 42);
223 assert_eq!(*counter.lock().unwrap(), 1);
224 }
225
226 #[test]
227 fn test_lazy_ref_reload() {
228 let lazy: LazyRef<i32> = LazyRef::new(|| 1);
229 assert_eq!(lazy.get().unwrap(), 1);
230 assert_eq!(lazy.get().unwrap(), 1);
231 }
232
233 #[test]
234 fn test_lazy_ref_invalidate() {
235 let counter = Arc::new(Mutex::new(0));
236 let c = Arc::clone(&counter);
237 let lazy: LazyRef<i32> = LazyRef::new(move || {
238 let mut g = c.lock().unwrap();
239 *g += 1;
240 *g
241 });
242
243 assert_eq!(lazy.get().unwrap(), 1);
244 lazy.invalidate();
245 assert!(!lazy.is_loaded());
246 assert_eq!(lazy.get().unwrap(), 2);
247 }
248
249 #[test]
250 fn test_lazy_ref_loaded() {
251 let lazy: LazyRef<String> = LazyRef::loaded("preloaded".to_string());
252 assert!(lazy.is_loaded());
253 assert_eq!(lazy.get().unwrap(), "preloaded");
254 }
255
256 #[test]
257 fn test_lazy_ref_clone_shares_state() {
258 let lazy: LazyRef<i32> = LazyRef::new(|| 99);
259 let lazy2 = lazy.clone();
260 lazy.get();
261 assert!(lazy2.is_loaded());
262 }
263
264 #[test]
265 fn test_lazy_collection_basic() {
266 let coll: LazyCollection<i32> = LazyCollection::new(|| vec![1, 2, 3]);
267 assert!(!coll.is_loaded());
268 assert_eq!(coll.len(), 3);
269 assert!(!coll.is_empty());
270 assert!(coll.is_loaded());
271 }
272
273 #[test]
274 fn test_lazy_collection_filter() {
275 let coll: LazyCollection<i32> = LazyCollection::new(|| vec![1, 2, 3, 4, 5]);
276 let evens = coll.filter(|x| x % 2 == 0);
277 assert_eq!(evens, vec![2, 4]);
278 }
279
280 #[test]
281 fn test_lazy_collection_empty() {
282 let coll: LazyCollection<i32> = LazyCollection::new(std::vec::Vec::new);
283 assert_eq!(coll.len(), 0);
284 assert!(coll.is_empty());
285 }
286
287 #[test]
288 fn test_lazy_collection_loaded() {
289 let coll: LazyCollection<i32> = LazyCollection::loaded(vec![10, 20]);
290 assert!(coll.is_loaded());
291 assert_eq!(coll.all(), vec![10, 20]);
292 }
293
294 #[test]
295 fn test_lazy_loader_count() {
296 let loader = LazyLoader::new();
297 let lazy1: LazyRef<i32> = loader.lazy(|| 1);
298 let lazy2: LazyRef<i32> = loader.lazy(|| 2);
299
300 assert_eq!(loader.load_count(), 0);
301 lazy1.get();
302 assert_eq!(loader.load_count(), 1);
303 lazy2.get();
304 assert_eq!(loader.load_count(), 2);
305 lazy1.get();
306 assert_eq!(loader.load_count(), 2);
307 }
308
309 #[test]
310 fn test_lazy_loader_reset() {
311 let loader = LazyLoader::new();
312 let lazy: LazyRef<i32> = loader.lazy(|| 42);
313 lazy.get();
314 assert_eq!(loader.load_count(), 1);
315 loader.reset_count();
316 assert_eq!(loader.load_count(), 0);
317 }
318
319 #[test]
320 fn test_e2e_user_orders_lazy_loading() {
321 #[derive(Clone)]
322 #[allow(dead_code)]
323 struct Order {
324 id: i64,
325 user_id: i64,
326 amount: f64,
327 }
328
329 #[derive(Clone)]
330 #[allow(dead_code)]
331 struct User {
332 id: i64,
333 name: String,
334 orders: LazyCollection<Order>,
335 }
336
337 let query_count = Arc::new(Mutex::new(0));
338 let qc = Arc::clone(&query_count);
339
340 let user = User {
341 id: 1,
342 name: "alice".into(),
343 orders: LazyCollection::new(move || {
344 *qc.lock().unwrap() += 1;
345 vec![
346 Order {
347 id: 101,
348 user_id: 1,
349 amount: 99.5,
350 },
351 Order {
352 id: 102,
353 user_id: 1,
354 amount: 200.0,
355 },
356 ]
357 }),
358 };
359
360 assert!(!user.orders.is_loaded());
361 assert_eq!(*query_count.lock().unwrap(), 0);
362
363 let all_orders = user.orders.all();
364 assert_eq!(all_orders.len(), 2);
365 assert_eq!(all_orders[0].id, 101);
366 assert_eq!(all_orders[1].amount, 200.0);
367 assert!(user.orders.is_loaded());
368 assert_eq!(*query_count.lock().unwrap(), 1);
369
370 let _again = user.orders.all();
371 assert_eq!(*query_count.lock().unwrap(), 1);
372
373 let big_orders = user.orders.filter(|o| o.amount >= 200.0);
374 assert_eq!(big_orders.len(), 1);
375 assert_eq!(big_orders[0].id, 102);
376 assert_eq!(*query_count.lock().unwrap(), 1);
377 }
378
379 #[test]
380 fn test_e2e_lazy_ref_belongs_to() {
381 #[derive(Clone)]
382 #[allow(dead_code)]
383 struct User {
384 id: i64,
385 name: String,
386 }
387
388 #[derive(Clone)]
389 #[allow(dead_code)]
390 struct Order {
391 id: i64,
392 user: LazyRef<User>,
393 }
394
395 let user = Order {
396 id: 501,
397 user: LazyRef::new(|| User {
398 id: 1,
399 name: "alice".into(),
400 }),
401 };
402
403 assert!(!user.user.is_loaded());
404 let u = user.user.get().unwrap();
405 assert_eq!(u.name, "alice");
406 assert!(user.user.is_loaded());
407
408 user.user.invalidate();
409 assert!(!user.user.is_loaded());
410 let u2 = user.user.get().unwrap();
411 assert_eq!(u2.id, 1);
412 }
413
414 #[test]
415 fn test_e2e_lazy_loader_multi_entity_tracking() {
416 let loader = LazyLoader::new();
417
418 let lazy_profile: LazyRef<String> = loader.lazy(|| "alice profile".into());
419 let lazy_orders: LazyRef<Vec<i64>> = loader.lazy(|| vec![1, 2, 3]);
420
421 assert_eq!(loader.load_count(), 0);
422
423 lazy_profile.get();
424 assert_eq!(loader.load_count(), 1);
425
426 let _ = lazy_orders.get();
427 assert_eq!(loader.load_count(), 2);
428
429 lazy_profile.get();
430 let _ = lazy_orders.get();
431 assert_eq!(loader.load_count(), 2);
432
433 lazy_orders.invalidate();
434 let _ = lazy_orders.get();
435 assert_eq!(loader.load_count(), 3);
436 }
437}