1use std::cell::UnsafeCell;
24use std::fmt;
25use std::future::Future;
26use std::pin::Pin;
27use std::sync::Arc;
28use std::sync::atomic::{AtomicU8, Ordering};
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
132 .store(LazyState::Unloaded as u8, Ordering::Release);
133 self.value.get_mut().take()
134 } else {
135 None
136 }
137 }
138
139 pub fn reset(&mut self) {
141 self.state
142 .store(LazyState::Unloaded as u8, Ordering::Release);
143 *self.value.get_mut() = None;
144 }
145
146 pub async fn load_with<F, Fut, E>(&self, loader: F) -> Result<&T, E>
151 where
152 F: FnOnce() -> Fut,
153 Fut: Future<Output = Result<T, E>>,
154 {
155 if self.is_loaded() {
157 return Ok(unsafe { (*self.value.get()).as_ref().unwrap() });
159 }
160
161 let prev = self.state.compare_exchange(
163 LazyState::Unloaded as u8,
164 LazyState::Loading as u8,
165 Ordering::AcqRel,
166 Ordering::Acquire,
167 );
168
169 match prev {
170 Ok(_) => {
171 match loader().await {
173 Ok(value) => {
174 unsafe {
176 *self.value.get() = Some(value);
177 }
178 self.state.store(LazyState::Loaded as u8, Ordering::Release);
179 Ok(unsafe { (*self.value.get()).as_ref().unwrap() })
181 }
182 Err(e) => {
183 self.state
184 .store(LazyState::Unloaded as u8, Ordering::Release);
185 Err(e)
186 }
187 }
188 }
189 Err(current) => {
190 match LazyState::from(current) {
192 LazyState::Loaded => {
193 Ok(unsafe { (*self.value.get()).as_ref().unwrap() })
195 }
196 LazyState::Loading => {
197 loop {
199 tokio::task::yield_now().await;
200 let state = LazyState::from(self.state.load(Ordering::Acquire));
201 match state {
202 LazyState::Loaded => {
203 return Ok(unsafe { (*self.value.get()).as_ref().unwrap() });
204 }
205 LazyState::Unloaded | LazyState::Failed => {
206 return Box::pin(self.load_with(loader)).await;
208 }
209 LazyState::Loading => continue,
210 }
211 }
212 }
213 _ => {
214 Box::pin(self.load_with(loader)).await
216 }
217 }
218 }
219 }
220 }
221}
222
223impl<T: Default> Default for Lazy<T> {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229impl<T: Clone> Clone for Lazy<T> {
230 fn clone(&self) -> Self {
231 if self.is_loaded() {
232 Self::loaded(self.get().unwrap().clone())
233 } else {
234 Self::new()
235 }
236 }
237}
238
239impl<T: fmt::Debug> fmt::Debug for Lazy<T> {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 let state = LazyState::from(self.state.load(Ordering::Acquire));
242 match state {
243 LazyState::Loaded => {
244 if let Some(value) = self.get() {
245 f.debug_struct("Lazy")
246 .field("state", &"Loaded")
247 .field("value", value)
248 .finish()
249 } else {
250 f.debug_struct("Lazy").field("state", &"Loaded").finish()
251 }
252 }
253 _ => f.debug_struct("Lazy").field("state", &state).finish(),
254 }
255 }
256}
257
258pub struct LazyRelation<T, L> {
262 pub value: Lazy<T>,
264 pub loader: L,
266}
267
268impl<T, L> LazyRelation<T, L> {
269 pub fn new(loader: L) -> Self {
271 Self {
272 value: Lazy::new(),
273 loader,
274 }
275 }
276
277 pub fn loaded(value: T, loader: L) -> Self {
279 Self {
280 value: Lazy::loaded(value),
281 loader,
282 }
283 }
284
285 #[inline]
287 pub fn is_loaded(&self) -> bool {
288 self.value.is_loaded()
289 }
290
291 pub fn get(&self) -> Option<&T> {
293 self.value.get()
294 }
295}
296
297impl<T: Clone, L: Clone> Clone for LazyRelation<T, L> {
298 fn clone(&self) -> Self {
299 Self {
300 value: self.value.clone(),
301 loader: self.loader.clone(),
302 }
303 }
304}
305
306impl<T: fmt::Debug, L: fmt::Debug> fmt::Debug for LazyRelation<T, L> {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 f.debug_struct("LazyRelation")
309 .field("value", &self.value)
310 .field("loader", &self.loader)
311 .finish()
312 }
313}
314
315#[derive(Debug, Clone)]
317pub struct OneToManyLoader {
318 pub foreign_key: String,
320 pub local_key_value: crate::filter::FilterValue,
322 pub table: String,
324}
325
326impl OneToManyLoader {
327 pub fn new(
329 table: impl Into<String>,
330 foreign_key: impl Into<String>,
331 local_key_value: impl Into<crate::filter::FilterValue>,
332 ) -> Self {
333 Self {
334 table: table.into(),
335 foreign_key: foreign_key.into(),
336 local_key_value: local_key_value.into(),
337 }
338 }
339}
340
341#[derive(Debug, Clone)]
343pub struct ManyToOneLoader {
344 pub foreign_key_value: crate::filter::FilterValue,
346 pub table: String,
348 pub primary_key: String,
350}
351
352impl ManyToOneLoader {
353 pub fn new(
355 table: impl Into<String>,
356 primary_key: impl Into<String>,
357 foreign_key_value: impl Into<crate::filter::FilterValue>,
358 ) -> Self {
359 Self {
360 table: table.into(),
361 primary_key: primary_key.into(),
362 foreign_key_value: foreign_key_value.into(),
363 }
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_lazy_new() {
373 let lazy: Lazy<i32> = Lazy::new();
374 assert!(!lazy.is_loaded());
375 assert!(lazy.get().is_none());
376 }
377
378 #[test]
379 fn test_lazy_loaded() {
380 let lazy = Lazy::loaded(42);
381 assert!(lazy.is_loaded());
382 assert_eq!(lazy.get(), Some(&42));
383 }
384
385 #[test]
386 fn test_lazy_set() {
387 let lazy: Lazy<i32> = Lazy::new();
388 lazy.set(42);
389 assert!(lazy.is_loaded());
390 assert_eq!(lazy.get(), Some(&42));
391 }
392
393 #[test]
394 fn test_lazy_take() {
395 let mut lazy = Lazy::loaded(42);
396 let value = lazy.take();
397 assert_eq!(value, Some(42));
398 assert!(!lazy.is_loaded());
399 }
400
401 #[test]
402 fn test_lazy_reset() {
403 let mut lazy = Lazy::loaded(42);
404 lazy.reset();
405 assert!(!lazy.is_loaded());
406 assert!(lazy.get().is_none());
407 }
408
409 #[test]
410 fn test_lazy_clone() {
411 let lazy = Lazy::loaded(42);
412 let cloned = lazy.clone();
413 assert!(cloned.is_loaded());
414 assert_eq!(cloned.get(), Some(&42));
415 }
416
417 #[test]
418 fn test_lazy_clone_unloaded() {
419 let lazy: Lazy<i32> = Lazy::new();
420 let cloned = lazy.clone();
421 assert!(!cloned.is_loaded());
422 }
423
424 #[tokio::test]
425 async fn test_lazy_load_with() {
426 let lazy: Lazy<i32> = Lazy::new();
427
428 let result = lazy.load_with(|| async { Ok::<_, &str>(42) }).await;
429
430 assert!(result.is_ok());
431 assert_eq!(result.unwrap(), &42);
432 assert!(lazy.is_loaded());
433 }
434
435 #[tokio::test]
436 async fn test_lazy_load_cached() {
437 let lazy = Lazy::loaded(42);
438
439 let result = lazy.load_with(|| async { Ok::<_, &str>(100) }).await;
441
442 assert!(result.is_ok());
443 assert_eq!(result.unwrap(), &42); }
445
446 #[test]
447 fn test_lazy_relation() {
448 let relation: LazyRelation<Vec<i32>, OneToManyLoader> =
449 LazyRelation::new(OneToManyLoader::new("posts", "user_id", 1i64));
450
451 assert!(!relation.is_loaded());
452 assert!(relation.get().is_none());
453 }
454}