1#[fp_macros::document_module]
6mod inner {
7 use crate::{
8 brands::TryLazyBrand,
9 classes::{Deferrable, SendDeferrable},
10 impl_kind,
11 kinds::*,
12 types::{ArcLazyConfig, Lazy, LazyConfig, RcLazyConfig, TryThunk, TryTrampoline},
13 };
14 use fp_macros::{document_fields, document_parameters, document_type_parameters};
15
16 #[document_type_parameters(
22 "The lifetime of the computation.",
23 "The type of the computed value.",
24 "The type of the error.",
25 "The memoization configuration."
26 )]
27 #[document_fields("The internal lazy cell.")]
36 pub struct TryLazy<'a, A, E, Config: LazyConfig = RcLazyConfig>(
37 pub(crate) Config::TryLazy<'a, A, E>,
38 )
39 where
40 A: 'a,
41 E: 'a;
42
43 #[document_type_parameters(
46 "The lifetime of the computation.",
47 "The type of the computed value.",
48 "The type of the error.",
49 "The memoization configuration."
50 )]
51 #[document_parameters("The instance to clone.")]
52 impl<'a, A, E, Config: LazyConfig> Clone for TryLazy<'a, A, E, Config>
53 where
54 A: 'a,
55 E: 'a,
56 {
57 #[document_signature]
60 fn clone(&self) -> Self {
61 Self(self.0.clone())
62 }
63 }
64
65 #[document_type_parameters(
68 "The lifetime of the computation.",
69 "The type of the computed value.",
70 "The type of the error.",
71 "The memoization configuration."
72 )]
73 #[document_parameters("The `TryLazy` instance.")]
74 impl<'a, A, E, Config: LazyConfig> TryLazy<'a, A, E, Config>
75 where
76 A: 'a,
77 E: 'a,
78 {
79 #[document_signature]
84 pub fn evaluate(&self) -> Result<&A, &E> {
98 Config::try_evaluate(&self.0)
99 }
100 }
101
102 #[document_type_parameters(
105 "The lifetime of the computation.",
106 "The type of the computed value.",
107 "The type of the error."
108 )]
109 impl<'a, A, E> TryLazy<'a, A, E, RcLazyConfig>
110 where
111 A: 'a,
112 E: 'a,
113 {
114 #[document_signature]
119 #[document_type_parameters("The type of the initializer closure.")]
123 #[document_parameters("The closure that produces the result.")]
127 pub fn new<F>(f: F) -> Self
141 where
142 F: FnOnce() -> Result<A, E> + 'a,
143 {
144 TryLazy(RcLazyConfig::try_lazy_new(Box::new(f)))
145 }
146 }
147
148 #[document_type_parameters(
151 "The lifetime of the computation.",
152 "The type of the computed value.",
153 "The type of the error."
154 )]
155 impl<'a, A, E> From<TryThunk<'a, A, E>> for TryLazy<'a, A, E, RcLazyConfig> {
156 #[document_signature]
159 #[document_parameters("The fallible thunk to convert.")]
163 fn from(eval: TryThunk<'a, A, E>) -> Self {
164 Self::new(move || eval.evaluate())
165 }
166 }
167
168 #[document_type_parameters(
171 "The lifetime of the computation.",
172 "The type of the computed value.",
173 "The type of the error."
174 )]
175 impl<'a, A, E> From<TryTrampoline<A, E>> for TryLazy<'a, A, E, RcLazyConfig>
176 where
177 A: Send,
178 E: Send,
179 {
180 #[document_signature]
183 #[document_parameters("The fallible trampoline to convert.")]
187 fn from(task: TryTrampoline<A, E>) -> Self {
188 Self::new(move || task.evaluate())
189 }
190 }
191
192 #[document_type_parameters(
195 "The lifetime of the computation.",
196 "The type of the computed value.",
197 "The type of the error."
198 )]
199 impl<'a, A, E> From<Lazy<'a, A, ArcLazyConfig>> for TryLazy<'a, A, E, ArcLazyConfig>
200 where
201 A: Clone + Send + Sync + 'a,
202 E: Send + Sync + 'a,
203 {
204 #[document_signature]
207 #[document_parameters("The thread-safe lazy value to convert.")]
211 fn from(memo: Lazy<'a, A, ArcLazyConfig>) -> Self {
212 Self::new(move || Ok(memo.evaluate().clone()))
213 }
214 }
215
216 #[document_type_parameters(
219 "The lifetime of the computation.",
220 "The type of the computed value.",
221 "The type of the error."
222 )]
223 impl<'a, A, E> From<Lazy<'a, A, RcLazyConfig>> for TryLazy<'a, A, E, RcLazyConfig>
224 where
225 A: Clone + 'a,
226 E: 'a,
227 {
228 #[document_signature]
231 #[document_parameters("The lazy value to convert.")]
235 fn from(memo: Lazy<'a, A, RcLazyConfig>) -> Self {
236 Self::new(move || Ok(memo.evaluate().clone()))
237 }
238 }
239
240 #[document_type_parameters(
243 "The lifetime of the computation.",
244 "The type of the computed value."
245 )]
246 impl<'a, A> TryLazy<'a, A, String, RcLazyConfig>
247 where
248 A: 'a,
249 {
250 #[document_signature]
255 #[document_type_parameters("The type of the initializer closure.")]
259 #[document_parameters("The closure that might panic.")]
263 pub fn catch_unwind<F>(f: F) -> Self
280 where
281 F: FnOnce() -> A + std::panic::UnwindSafe + 'a,
282 {
283 Self::new(move || {
284 std::panic::catch_unwind(f).map_err(|e| {
285 if let Some(s) = e.downcast_ref::<&str>() {
286 s.to_string()
287 } else if let Some(s) = e.downcast_ref::<String>() {
288 s.clone()
289 } else {
290 "Unknown panic".to_string()
291 }
292 })
293 })
294 }
295 }
296
297 #[document_type_parameters(
300 "The lifetime of the computation.",
301 "The type of the computed value.",
302 "The type of the error."
303 )]
304 impl<'a, A, E> TryLazy<'a, A, E, ArcLazyConfig>
305 where
306 A: 'a,
307 E: 'a,
308 {
309 #[document_signature]
314 #[document_type_parameters("The type of the initializer closure.")]
318 #[document_parameters("The closure that produces the result.")]
322 pub fn new<F>(f: F) -> Self
336 where
337 F: FnOnce() -> Result<A, E> + Send + 'a,
338 {
339 TryLazy(ArcLazyConfig::try_lazy_new(Box::new(f)))
340 }
341 }
342
343 #[document_type_parameters(
346 "The lifetime of the computation.",
347 "The type of the computed value.",
348 "The type of the error."
349 )]
350 impl<'a, A, E> Deferrable<'a> for TryLazy<'a, A, E, RcLazyConfig>
351 where
352 A: Clone + 'a,
353 E: Clone + 'a,
354 {
355 #[document_signature]
363 #[document_type_parameters("The type of the thunk.")]
367 #[document_parameters("The thunk that produces the lazy value.")]
371 fn defer<F>(f: F) -> Self
385 where
386 F: FnOnce() -> Self + 'a,
387 Self: Sized,
388 {
389 Self::new(move || f().evaluate().cloned().map_err(Clone::clone))
390 }
391 }
392
393 impl_kind! {
394 impl<E: 'static, Config: LazyConfig> for TryLazyBrand<E, Config> {
395 #[document_default]
396 type Of<'a, A: 'a>: 'a = TryLazy<'a, A, E, Config>;
397 }
398 }
399
400 #[document_type_parameters(
403 "The lifetime of the computation.",
404 "The type of the computed value.",
405 "The type of the error."
406 )]
407 impl<'a, A, E> SendDeferrable<'a> for TryLazy<'a, A, E, ArcLazyConfig>
408 where
409 A: Clone + Send + Sync + 'a,
410 E: Clone + Send + Sync + 'a,
411 {
412 #[document_signature]
420 #[document_type_parameters("The type of the thunk.")]
424 #[document_parameters("The thunk that produces the lazy value.")]
428 fn send_defer<F>(f: F) -> Self
442 where
443 F: FnOnce() -> Self + Send + Sync + 'a,
444 Self: Sized,
445 {
446 Self::new(move || f().evaluate().cloned().map_err(Clone::clone))
447 }
448 }
449
450 pub type RcTryLazy<'a, A, E> = TryLazy<'a, A, E, RcLazyConfig>;
452
453 pub type ArcTryLazy<'a, A, E> = TryLazy<'a, A, E, ArcLazyConfig>;
455}
456pub use inner::*;
457
458#[cfg(test)]
459mod tests {
460 use crate::types::{RcLazy, TryThunk, TryTrampoline};
461
462 use super::*;
463 use std::cell::RefCell;
464 use std::rc::Rc;
465
466 #[test]
470 fn test_try_memo_caching_ok() {
471 let counter = Rc::new(RefCell::new(0));
472 let counter_clone = counter.clone();
473 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
474 *counter_clone.borrow_mut() += 1;
475 Ok(42)
476 });
477
478 assert_eq!(*counter.borrow(), 0);
479 assert_eq!(memo.evaluate(), Ok(&42));
480 assert_eq!(*counter.borrow(), 1);
481 assert_eq!(memo.evaluate(), Ok(&42));
482 assert_eq!(*counter.borrow(), 1);
483 }
484
485 #[test]
489 fn test_try_memo_caching_err() {
490 let counter = Rc::new(RefCell::new(0));
491 let counter_clone = counter.clone();
492 let memo: RcTryLazy<i32, i32> = RcTryLazy::new(move || {
493 *counter_clone.borrow_mut() += 1;
494 Err(0)
495 });
496
497 assert_eq!(*counter.borrow(), 0);
498 assert_eq!(memo.evaluate(), Err(&0));
499 assert_eq!(*counter.borrow(), 1);
500 assert_eq!(memo.evaluate(), Err(&0));
501 assert_eq!(*counter.borrow(), 1);
502 }
503
504 #[test]
508 fn test_try_memo_sharing() {
509 let counter = Rc::new(RefCell::new(0));
510 let counter_clone = counter.clone();
511 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
512 *counter_clone.borrow_mut() += 1;
513 Ok(42)
514 });
515 let shared = memo.clone();
516
517 assert_eq!(memo.evaluate(), Ok(&42));
518 assert_eq!(*counter.borrow(), 1);
519 assert_eq!(shared.evaluate(), Ok(&42));
520 assert_eq!(*counter.borrow(), 1);
521 }
522
523 #[test]
527 fn test_catch_unwind() {
528 let memo = RcTryLazy::catch_unwind(|| {
529 if true {
530 panic!("oops")
531 }
532 42
533 });
534
535 match memo.evaluate() {
536 Err(e) => assert_eq!(e, "oops"),
537 Ok(_) => panic!("Should have failed"),
538 }
539 }
540
541 #[test]
543 fn test_try_memo_from_try_eval() {
544 let eval = TryThunk::new(|| Ok::<i32, ()>(42));
545 let memo = RcTryLazy::from(eval);
546 assert_eq!(memo.evaluate(), Ok(&42));
547 }
548
549 #[test]
551 fn test_try_memo_from_try_task() {
552 let task = TryTrampoline::<i32, ()>::ok(42);
553 let memo = RcTryLazy::from(task);
554 assert_eq!(memo.evaluate(), Ok(&42));
555 }
556
557 #[test]
559 fn test_try_memo_from_rc_memo() {
560 let memo = RcLazy::new(|| 42);
561 let try_memo: crate::types::RcTryLazy<i32, ()> = crate::types::RcTryLazy::from(memo);
562 assert_eq!(try_memo.evaluate(), Ok(&42));
563 }
564
565 #[test]
567 fn test_try_memo_from_arc_memo() {
568 use crate::types::ArcLazy;
569 let memo = ArcLazy::new(|| 42);
570 let try_memo: crate::types::ArcTryLazy<i32, ()> = crate::types::ArcTryLazy::from(memo);
571 assert_eq!(try_memo.evaluate(), Ok(&42));
572 }
573
574 #[test]
576 fn test_send_defer() {
577 use crate::classes::send_deferrable::send_defer;
578
579 let memo: ArcTryLazy<i32, ()> = send_defer(|| ArcTryLazy::new(|| Ok(42)));
580 assert_eq!(memo.evaluate(), Ok(&42));
581 }
582}