1use std::cell::UnsafeCell;
24use std::fmt;
25use std::future::Future;
26use std::sync::atomic::{AtomicU8, Ordering};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[repr(u8)]
31enum LazyState {
32 Unloaded = 0,
34 Loading = 1,
36 Loaded = 2,
38 Failed = 3,
40}
41
42impl From<u8> for LazyState {
43 fn from(v: u8) -> Self {
44 match v {
45 0 => Self::Unloaded,
46 1 => Self::Loading,
47 2 => Self::Loaded,
48 3 => Self::Failed,
49 _ => Self::Unloaded,
50 }
51 }
52}
53
54pub struct Lazy<T> {
59 state: AtomicU8,
60 value: UnsafeCell<Option<T>>,
61}
62
63unsafe impl<T: Send> Send for Lazy<T> {}
66unsafe impl<T: Sync> Sync for Lazy<T> {}
67
68impl<T> Lazy<T> {
69 pub const fn new() -> Self {
71 Self {
72 state: AtomicU8::new(LazyState::Unloaded as u8),
73 value: UnsafeCell::new(None),
74 }
75 }
76
77 pub fn loaded(value: T) -> Self {
79 Self {
80 state: AtomicU8::new(LazyState::Loaded as u8),
81 value: UnsafeCell::new(Some(value)),
82 }
83 }
84
85 #[inline]
87 pub fn is_loaded(&self) -> bool {
88 LazyState::from(self.state.load(Ordering::Acquire)) == LazyState::Loaded
89 }
90
91 #[inline]
93 pub fn is_loading(&self) -> bool {
94 LazyState::from(self.state.load(Ordering::Acquire)) == LazyState::Loading
95 }
96
97 pub fn get(&self) -> Option<&T> {
99 if self.is_loaded() {
100 unsafe { (*self.value.get()).as_ref() }
103 } else {
104 None
105 }
106 }
107
108 pub fn get_mut(&mut self) -> Option<&mut T> {
110 if self.is_loaded() {
111 self.value.get_mut().as_mut()
112 } else {
113 None
114 }
115 }
116
117 pub fn set(&self, value: T) {
119 unsafe {
121 *self.value.get() = Some(value);
122 }
123 self.state.store(LazyState::Loaded as u8, Ordering::Release);
124 }
125
126 pub fn take(&mut self) -> Option<T> {
128 if self.is_loaded() {
129 self.state
130 .store(LazyState::Unloaded as u8, Ordering::Release);
131 self.value.get_mut().take()
132 } else {
133 None
134 }
135 }
136
137 pub fn reset(&mut self) {
139 self.state
140 .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
182 .store(LazyState::Unloaded as u8, Ordering::Release);
183 Err(e)
184 }
185 }
186 }
187 Err(current) => {
188 match LazyState::from(current) {
190 LazyState::Loaded => {
191 Ok(unsafe { (*self.value.get()).as_ref().unwrap() })
193 }
194 LazyState::Loading => {
195 loop {
197 tokio::task::yield_now().await;
198 let state = LazyState::from(self.state.load(Ordering::Acquire));
199 match state {
200 LazyState::Loaded => {
201 return Ok(unsafe { (*self.value.get()).as_ref().unwrap() });
202 }
203 LazyState::Unloaded | LazyState::Failed => {
204 return Box::pin(self.load_with(loader)).await;
206 }
207 LazyState::Loading => continue,
208 }
209 }
210 }
211 _ => {
212 Box::pin(self.load_with(loader)).await
214 }
215 }
216 }
217 }
218 }
219}
220
221impl<T: Default> Default for Lazy<T> {
222 fn default() -> Self {
223 Self::new()
224 }
225}
226
227impl<T: Clone> Clone for Lazy<T> {
228 fn clone(&self) -> Self {
229 if self.is_loaded() {
230 Self::loaded(self.get().unwrap().clone())
231 } else {
232 Self::new()
233 }
234 }
235}
236
237impl<T: fmt::Debug> fmt::Debug for Lazy<T> {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 let state = LazyState::from(self.state.load(Ordering::Acquire));
240 match state {
241 LazyState::Loaded => {
242 if let Some(value) = self.get() {
243 f.debug_struct("Lazy")
244 .field("state", &"Loaded")
245 .field("value", value)
246 .finish()
247 } else {
248 f.debug_struct("Lazy").field("state", &"Loaded").finish()
249 }
250 }
251 _ => f.debug_struct("Lazy").field("state", &state).finish(),
252 }
253 }
254}
255
256pub struct LazyRelation<T, L> {
260 pub value: Lazy<T>,
262 pub loader: L,
264}
265
266impl<T, L> LazyRelation<T, L> {
267 pub fn new(loader: L) -> Self {
269 Self {
270 value: Lazy::new(),
271 loader,
272 }
273 }
274
275 pub fn loaded(value: T, loader: L) -> Self {
277 Self {
278 value: Lazy::loaded(value),
279 loader,
280 }
281 }
282
283 #[inline]
285 pub fn is_loaded(&self) -> bool {
286 self.value.is_loaded()
287 }
288
289 pub fn get(&self) -> Option<&T> {
291 self.value.get()
292 }
293}
294
295impl<T: Clone, L: Clone> Clone for LazyRelation<T, L> {
296 fn clone(&self) -> Self {
297 Self {
298 value: self.value.clone(),
299 loader: self.loader.clone(),
300 }
301 }
302}
303
304impl<T: fmt::Debug, L: fmt::Debug> fmt::Debug for LazyRelation<T, L> {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 f.debug_struct("LazyRelation")
307 .field("value", &self.value)
308 .field("loader", &self.loader)
309 .finish()
310 }
311}
312
313#[derive(Debug, Clone)]
315pub struct OneToManyLoader {
316 pub foreign_key: String,
318 pub local_key_value: crate::filter::FilterValue,
320 pub table: String,
322}
323
324impl OneToManyLoader {
325 pub fn new(
327 table: impl Into<String>,
328 foreign_key: impl Into<String>,
329 local_key_value: impl Into<crate::filter::FilterValue>,
330 ) -> Self {
331 Self {
332 table: table.into(),
333 foreign_key: foreign_key.into(),
334 local_key_value: local_key_value.into(),
335 }
336 }
337}
338
339#[derive(Debug, Clone)]
341pub struct ManyToOneLoader {
342 pub foreign_key_value: crate::filter::FilterValue,
344 pub table: String,
346 pub primary_key: String,
348}
349
350impl ManyToOneLoader {
351 pub fn new(
353 table: impl Into<String>,
354 primary_key: impl Into<String>,
355 foreign_key_value: impl Into<crate::filter::FilterValue>,
356 ) -> Self {
357 Self {
358 table: table.into(),
359 primary_key: primary_key.into(),
360 foreign_key_value: foreign_key_value.into(),
361 }
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn test_lazy_new() {
371 let lazy: Lazy<i32> = Lazy::new();
372 assert!(!lazy.is_loaded());
373 assert!(lazy.get().is_none());
374 }
375
376 #[test]
377 fn test_lazy_loaded() {
378 let lazy = Lazy::loaded(42);
379 assert!(lazy.is_loaded());
380 assert_eq!(lazy.get(), Some(&42));
381 }
382
383 #[test]
384 fn test_lazy_set() {
385 let lazy: Lazy<i32> = Lazy::new();
386 lazy.set(42);
387 assert!(lazy.is_loaded());
388 assert_eq!(lazy.get(), Some(&42));
389 }
390
391 #[test]
392 fn test_lazy_take() {
393 let mut lazy = Lazy::loaded(42);
394 let value = lazy.take();
395 assert_eq!(value, Some(42));
396 assert!(!lazy.is_loaded());
397 }
398
399 #[test]
400 fn test_lazy_reset() {
401 let mut lazy = Lazy::loaded(42);
402 lazy.reset();
403 assert!(!lazy.is_loaded());
404 assert!(lazy.get().is_none());
405 }
406
407 #[test]
408 fn test_lazy_clone() {
409 let lazy = Lazy::loaded(42);
410 let cloned = lazy.clone();
411 assert!(cloned.is_loaded());
412 assert_eq!(cloned.get(), Some(&42));
413 }
414
415 #[test]
416 fn test_lazy_clone_unloaded() {
417 let lazy: Lazy<i32> = Lazy::new();
418 let cloned = lazy.clone();
419 assert!(!cloned.is_loaded());
420 }
421
422 #[tokio::test]
423 async fn test_lazy_load_with() {
424 let lazy: Lazy<i32> = Lazy::new();
425
426 let result = lazy.load_with(|| async { Ok::<_, &str>(42) }).await;
427
428 assert!(result.is_ok());
429 assert_eq!(result.unwrap(), &42);
430 assert!(lazy.is_loaded());
431 }
432
433 #[tokio::test]
434 async fn test_lazy_load_cached() {
435 let lazy = Lazy::loaded(42);
436
437 let result = lazy.load_with(|| async { Ok::<_, &str>(100) }).await;
439
440 assert!(result.is_ok());
441 assert_eq!(result.unwrap(), &42); }
443
444 #[test]
445 fn test_lazy_relation() {
446 let relation: LazyRelation<Vec<i32>, OneToManyLoader> =
447 LazyRelation::new(OneToManyLoader::new("posts", "user_id", 1i64));
448
449 assert!(!relation.is_loaded());
450 assert!(relation.get().is_none());
451 }
452}