1use std::cell::UnsafeCell;
24use std::fmt;
25use std::future::Future;
26use std::pin::Pin;
27use std::sync::atomic::{AtomicU8, Ordering};
28use std::sync::Arc;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[repr(u8)]
33enum LazyState {
34 Unloaded = 0,
36 Loading = 1,
38 Loaded = 2,
40 Failed = 3,
42}
43
44impl From<u8> for LazyState {
45 fn from(v: u8) -> Self {
46 match v {
47 0 => Self::Unloaded,
48 1 => Self::Loading,
49 2 => Self::Loaded,
50 3 => Self::Failed,
51 _ => Self::Unloaded,
52 }
53 }
54}
55
56pub struct Lazy<T> {
61 state: AtomicU8,
62 value: UnsafeCell<Option<T>>,
63}
64
65unsafe impl<T: Send> Send for Lazy<T> {}
68unsafe impl<T: Sync> Sync for Lazy<T> {}
69
70impl<T> Lazy<T> {
71 pub const fn new() -> Self {
73 Self {
74 state: AtomicU8::new(LazyState::Unloaded as u8),
75 value: UnsafeCell::new(None),
76 }
77 }
78
79 pub fn loaded(value: T) -> Self {
81 Self {
82 state: AtomicU8::new(LazyState::Loaded as u8),
83 value: UnsafeCell::new(Some(value)),
84 }
85 }
86
87 #[inline]
89 pub fn is_loaded(&self) -> bool {
90 LazyState::from(self.state.load(Ordering::Acquire)) == LazyState::Loaded
91 }
92
93 #[inline]
95 pub fn is_loading(&self) -> bool {
96 LazyState::from(self.state.load(Ordering::Acquire)) == LazyState::Loading
97 }
98
99 pub fn get(&self) -> Option<&T> {
101 if self.is_loaded() {
102 unsafe { (*self.value.get()).as_ref() }
105 } else {
106 None
107 }
108 }
109
110 pub fn get_mut(&mut self) -> Option<&mut T> {
112 if self.is_loaded() {
113 self.value.get_mut().as_mut()
114 } else {
115 None
116 }
117 }
118
119 pub fn set(&self, value: T) {
121 unsafe {
123 *self.value.get() = Some(value);
124 }
125 self.state.store(LazyState::Loaded as u8, Ordering::Release);
126 }
127
128 pub fn take(&mut self) -> Option<T> {
130 if self.is_loaded() {
131 self.state.store(LazyState::Unloaded as u8, Ordering::Release);
132 self.value.get_mut().take()
133 } else {
134 None
135 }
136 }
137
138 pub fn reset(&mut self) {
140 self.state.store(LazyState::Unloaded as u8, Ordering::Release);
141 *self.value.get_mut() = None;
142 }
143
144 pub async fn load_with<F, Fut, E>(&self, loader: F) -> Result<&T, E>
149 where
150 F: FnOnce() -> Fut,
151 Fut: Future<Output = Result<T, E>>,
152 {
153 if self.is_loaded() {
155 return Ok(unsafe { (*self.value.get()).as_ref().unwrap() });
157 }
158
159 let prev = self.state.compare_exchange(
161 LazyState::Unloaded as u8,
162 LazyState::Loading as u8,
163 Ordering::AcqRel,
164 Ordering::Acquire,
165 );
166
167 match prev {
168 Ok(_) => {
169 match loader().await {
171 Ok(value) => {
172 unsafe {
174 *self.value.get() = Some(value);
175 }
176 self.state.store(LazyState::Loaded as u8, Ordering::Release);
177 Ok(unsafe { (*self.value.get()).as_ref().unwrap() })
179 }
180 Err(e) => {
181 self.state.store(LazyState::Unloaded as u8, Ordering::Release);
182 Err(e)
183 }
184 }
185 }
186 Err(current) => {
187 match LazyState::from(current) {
189 LazyState::Loaded => {
190 Ok(unsafe { (*self.value.get()).as_ref().unwrap() })
192 }
193 LazyState::Loading => {
194 loop {
196 tokio::task::yield_now().await;
197 let state = LazyState::from(self.state.load(Ordering::Acquire));
198 match state {
199 LazyState::Loaded => {
200 return Ok(unsafe { (*self.value.get()).as_ref().unwrap() });
201 }
202 LazyState::Unloaded | LazyState::Failed => {
203 return Box::pin(self.load_with(loader)).await;
205 }
206 LazyState::Loading => continue,
207 }
208 }
209 }
210 _ => {
211 Box::pin(self.load_with(loader)).await
213 }
214 }
215 }
216 }
217 }
218}
219
220impl<T: Default> Default for Lazy<T> {
221 fn default() -> Self {
222 Self::new()
223 }
224}
225
226impl<T: Clone> Clone for Lazy<T> {
227 fn clone(&self) -> Self {
228 if self.is_loaded() {
229 Self::loaded(self.get().unwrap().clone())
230 } else {
231 Self::new()
232 }
233 }
234}
235
236impl<T: fmt::Debug> fmt::Debug for Lazy<T> {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 let state = LazyState::from(self.state.load(Ordering::Acquire));
239 match state {
240 LazyState::Loaded => {
241 if let Some(value) = self.get() {
242 f.debug_struct("Lazy")
243 .field("state", &"Loaded")
244 .field("value", value)
245 .finish()
246 } else {
247 f.debug_struct("Lazy")
248 .field("state", &"Loaded")
249 .finish()
250 }
251 }
252 _ => f.debug_struct("Lazy").field("state", &state).finish(),
253 }
254 }
255}
256
257pub struct LazyRelation<T, L> {
261 pub value: Lazy<T>,
263 pub loader: L,
265}
266
267impl<T, L> LazyRelation<T, L> {
268 pub fn new(loader: L) -> Self {
270 Self {
271 value: Lazy::new(),
272 loader,
273 }
274 }
275
276 pub fn loaded(value: T, loader: L) -> Self {
278 Self {
279 value: Lazy::loaded(value),
280 loader,
281 }
282 }
283
284 #[inline]
286 pub fn is_loaded(&self) -> bool {
287 self.value.is_loaded()
288 }
289
290 pub fn get(&self) -> Option<&T> {
292 self.value.get()
293 }
294}
295
296impl<T: Clone, L: Clone> Clone for LazyRelation<T, L> {
297 fn clone(&self) -> Self {
298 Self {
299 value: self.value.clone(),
300 loader: self.loader.clone(),
301 }
302 }
303}
304
305impl<T: fmt::Debug, L: fmt::Debug> fmt::Debug for LazyRelation<T, L> {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 f.debug_struct("LazyRelation")
308 .field("value", &self.value)
309 .field("loader", &self.loader)
310 .finish()
311 }
312}
313
314#[derive(Debug, Clone)]
316pub struct OneToManyLoader {
317 pub foreign_key: String,
319 pub local_key_value: crate::filter::FilterValue,
321 pub table: String,
323}
324
325impl OneToManyLoader {
326 pub fn new(
328 table: impl Into<String>,
329 foreign_key: impl Into<String>,
330 local_key_value: impl Into<crate::filter::FilterValue>,
331 ) -> Self {
332 Self {
333 table: table.into(),
334 foreign_key: foreign_key.into(),
335 local_key_value: local_key_value.into(),
336 }
337 }
338}
339
340#[derive(Debug, Clone)]
342pub struct ManyToOneLoader {
343 pub foreign_key_value: crate::filter::FilterValue,
345 pub table: String,
347 pub primary_key: String,
349}
350
351impl ManyToOneLoader {
352 pub fn new(
354 table: impl Into<String>,
355 primary_key: impl Into<String>,
356 foreign_key_value: impl Into<crate::filter::FilterValue>,
357 ) -> Self {
358 Self {
359 table: table.into(),
360 primary_key: primary_key.into(),
361 foreign_key_value: foreign_key_value.into(),
362 }
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[test]
371 fn test_lazy_new() {
372 let lazy: Lazy<i32> = Lazy::new();
373 assert!(!lazy.is_loaded());
374 assert!(lazy.get().is_none());
375 }
376
377 #[test]
378 fn test_lazy_loaded() {
379 let lazy = Lazy::loaded(42);
380 assert!(lazy.is_loaded());
381 assert_eq!(lazy.get(), Some(&42));
382 }
383
384 #[test]
385 fn test_lazy_set() {
386 let lazy: Lazy<i32> = Lazy::new();
387 lazy.set(42);
388 assert!(lazy.is_loaded());
389 assert_eq!(lazy.get(), Some(&42));
390 }
391
392 #[test]
393 fn test_lazy_take() {
394 let mut lazy = Lazy::loaded(42);
395 let value = lazy.take();
396 assert_eq!(value, Some(42));
397 assert!(!lazy.is_loaded());
398 }
399
400 #[test]
401 fn test_lazy_reset() {
402 let mut lazy = Lazy::loaded(42);
403 lazy.reset();
404 assert!(!lazy.is_loaded());
405 assert!(lazy.get().is_none());
406 }
407
408 #[test]
409 fn test_lazy_clone() {
410 let lazy = Lazy::loaded(42);
411 let cloned = lazy.clone();
412 assert!(cloned.is_loaded());
413 assert_eq!(cloned.get(), Some(&42));
414 }
415
416 #[test]
417 fn test_lazy_clone_unloaded() {
418 let lazy: Lazy<i32> = Lazy::new();
419 let cloned = lazy.clone();
420 assert!(!cloned.is_loaded());
421 }
422
423 #[tokio::test]
424 async fn test_lazy_load_with() {
425 let lazy: Lazy<i32> = Lazy::new();
426
427 let result = lazy
428 .load_with(|| async { Ok::<_, &str>(42) })
429 .await;
430
431 assert!(result.is_ok());
432 assert_eq!(result.unwrap(), &42);
433 assert!(lazy.is_loaded());
434 }
435
436 #[tokio::test]
437 async fn test_lazy_load_cached() {
438 let lazy = Lazy::loaded(42);
439
440 let result = lazy
442 .load_with(|| async { Ok::<_, &str>(100) })
443 .await;
444
445 assert!(result.is_ok());
446 assert_eq!(result.unwrap(), &42); }
448
449 #[test]
450 fn test_lazy_relation() {
451 let relation: LazyRelation<Vec<i32>, OneToManyLoader> = LazyRelation::new(
452 OneToManyLoader::new("posts", "user_id", 1i64)
453 );
454
455 assert!(!relation.is_loaded());
456 assert!(relation.get().is_none());
457 }
458}
459