1#[fp_macros::document_module]
6mod inner {
7 use {
8 crate::{
9 brands::TryLazyBrand,
10 classes::{
11 Deferrable,
12 SendDeferrable,
13 },
14 impl_kind,
15 kinds::*,
16 types::{
17 ArcLazyConfig,
18 Lazy,
19 LazyConfig,
20 RcLazyConfig,
21 TryThunk,
22 TryTrampoline,
23 },
24 },
25 fp_macros::*,
26 };
27
28 #[document_type_parameters(
33 "The lifetime of the computation.",
34 "The type of the computed value.",
35 "The type of the error.",
36 "The memoization configuration."
37 )]
38 #[document_fields("The internal lazy cell.")]
44 pub struct TryLazy<'a, A, E, Config: LazyConfig = RcLazyConfig>(
45 pub(crate) Config::TryLazy<'a, A, E>,
46 )
47 where
48 A: 'a,
49 E: 'a;
50
51 #[document_type_parameters(
52 "The lifetime of the computation.",
53 "The type of the computed value.",
54 "The type of the error.",
55 "The memoization configuration."
56 )]
57 #[document_parameters("The instance to clone.")]
58 impl<'a, A, E, Config: LazyConfig> Clone for TryLazy<'a, A, E, Config>
59 where
60 A: 'a,
61 E: 'a,
62 {
63 #[document_signature]
64 #[document_returns(
65 "A new `TryLazy` instance that shares the same underlying memoized result."
66 )]
67 #[document_examples]
68 fn clone(&self) -> Self {
76 Self(self.0.clone())
77 }
78 }
79
80 #[document_type_parameters(
81 "The lifetime of the computation.",
82 "The type of the computed value.",
83 "The type of the error.",
84 "The memoization configuration."
85 )]
86 #[document_parameters("The `TryLazy` instance.")]
87 impl<'a, A, E, Config: LazyConfig> TryLazy<'a, A, E, Config>
88 where
89 A: 'a,
90 E: 'a,
91 {
92 #[document_signature]
94 #[document_returns("A result containing a reference to the value or error.")]
96 #[document_examples]
98 pub fn evaluate(&self) -> Result<&A, &E> {
106 Config::try_evaluate(&self.0)
107 }
108 }
109
110 #[document_type_parameters(
111 "The lifetime of the computation.",
112 "The type of the computed value.",
113 "The type of the error."
114 )]
115 impl<'a, A, E> TryLazy<'a, A, E, RcLazyConfig>
116 where
117 A: 'a,
118 E: 'a,
119 {
120 #[document_signature]
122 #[document_parameters("The closure that produces the result.")]
124 #[document_returns("A new `TryLazy` instance.")]
126 #[document_examples]
128 pub fn new(f: impl FnOnce() -> Result<A, E> + 'a) -> Self {
136 TryLazy(RcLazyConfig::try_lazy_new(Box::new(f)))
137 }
138 }
139
140 #[document_type_parameters(
141 "The lifetime of the computation.",
142 "The type of the computed value.",
143 "The type of the error."
144 )]
145 impl<'a, A, E> From<TryThunk<'a, A, E>> for TryLazy<'a, A, E, RcLazyConfig> {
146 #[document_signature]
147 #[document_parameters("The fallible thunk to convert.")]
148 #[document_returns(
149 "A new `TryLazy` instance that will evaluate the thunk on first access."
150 )]
151 #[document_examples]
152 fn from(eval: TryThunk<'a, A, E>) -> Self {
160 Self::new(move || eval.evaluate())
161 }
162 }
163
164 #[document_type_parameters(
165 "The lifetime of the computation.",
166 "The type of the computed value.",
167 "The type of the error."
168 )]
169 impl<'a, A, E> From<TryTrampoline<A, E>> for TryLazy<'a, A, E, RcLazyConfig>
170 where
171 A: Send,
172 E: Send,
173 {
174 #[document_signature]
175 #[document_parameters("The fallible trampoline to convert.")]
176 #[document_returns(
177 "A new `TryLazy` instance that will evaluate the trampoline on first access."
178 )]
179 #[document_examples]
180 fn from(task: TryTrampoline<A, E>) -> Self {
188 Self::new(move || task.evaluate())
189 }
190 }
191
192 #[document_type_parameters(
193 "The lifetime of the computation.",
194 "The type of the computed value.",
195 "The type of the error."
196 )]
197 impl<'a, A, E> From<Lazy<'a, A, ArcLazyConfig>> for TryLazy<'a, A, E, ArcLazyConfig>
198 where
199 A: Clone + Send + Sync + 'a,
200 E: Send + Sync + 'a,
201 {
202 #[document_signature]
203 #[document_parameters("The thread-safe lazy value to convert.")]
204 #[document_returns("A new `TryLazy` instance that wraps the lazy value.")]
205 #[document_examples]
206 fn from(memo: Lazy<'a, A, ArcLazyConfig>) -> Self {
214 Self::new(move || Ok(memo.evaluate().clone()))
215 }
216 }
217
218 #[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]
229 #[document_parameters("The lazy value to convert.")]
230 #[document_returns("A new `TryLazy` instance that wraps the lazy value.")]
231 #[document_examples]
232 fn from(memo: Lazy<'a, A, RcLazyConfig>) -> Self {
240 Self::new(move || Ok(memo.evaluate().clone()))
241 }
242 }
243
244 #[document_type_parameters(
245 "The lifetime of the computation.",
246 "The type of the computed value."
247 )]
248 impl<'a, A> TryLazy<'a, A, String, RcLazyConfig>
249 where
250 A: 'a,
251 {
252 #[document_signature]
254 #[document_parameters("The closure that might panic.")]
256 #[document_returns("A new `TryLazy` instance where panics are converted to `Err(String)`.")]
258 #[document_examples]
260 pub fn catch_unwind(f: impl FnOnce() -> A + std::panic::UnwindSafe + 'a) -> Self {
273 Self::new(move || {
274 std::panic::catch_unwind(f).map_err(|e| {
275 if let Some(s) = e.downcast_ref::<&str>() {
276 s.to_string()
277 } else if let Some(s) = e.downcast_ref::<String>() {
278 s.clone()
279 } else {
280 "Unknown panic".to_string()
281 }
282 })
283 })
284 }
285 }
286
287 #[document_type_parameters(
288 "The lifetime of the computation.",
289 "The type of the computed value.",
290 "The type of the error."
291 )]
292 impl<'a, A, E> TryLazy<'a, A, E, ArcLazyConfig>
293 where
294 A: 'a,
295 E: 'a,
296 {
297 #[document_signature]
299 #[document_parameters("The closure that produces the result.")]
301 #[document_returns("A new `TryLazy` instance.")]
303 #[document_examples]
305 pub fn new(f: impl FnOnce() -> Result<A, E> + Send + 'a) -> Self {
313 TryLazy(ArcLazyConfig::try_lazy_new(Box::new(f)))
314 }
315 }
316
317 #[document_type_parameters(
318 "The lifetime of the computation.",
319 "The type of the computed value.",
320 "The type of the error."
321 )]
322 impl<'a, A, E> Deferrable<'a> for TryLazy<'a, A, E, RcLazyConfig>
323 where
324 A: Clone + 'a,
325 E: Clone + 'a,
326 {
327 #[document_signature]
332 #[document_parameters("The thunk that produces the lazy value.")]
334 #[document_returns("A new `TryLazy` value.")]
336 #[document_examples]
338 fn defer(f: impl FnOnce() -> Self + 'a) -> Self
351 where
352 Self: Sized, {
353 Self::new(move || f().evaluate().cloned().map_err(Clone::clone))
354 }
355 }
356
357 impl_kind! {
358 impl<E: 'static, Config: LazyConfig> for TryLazyBrand<E, Config> {
359 #[document_default]
360 type Of<'a, A: 'a>: 'a = TryLazy<'a, A, E, Config>;
361 }
362 }
363
364 #[document_type_parameters(
365 "The lifetime of the computation.",
366 "The type of the computed value.",
367 "The type of the error."
368 )]
369 impl<'a, A, E> SendDeferrable<'a> for TryLazy<'a, A, E, ArcLazyConfig>
370 where
371 A: Clone + Send + Sync + 'a,
372 E: Clone + Send + Sync + 'a,
373 {
374 #[document_signature]
379 #[document_parameters("The thunk that produces the lazy value.")]
381 #[document_returns("A new `ArcTryLazy` value.")]
383 #[document_examples]
385 fn send_defer(f: impl FnOnce() -> Self + Send + Sync + 'a) -> Self
397 where
398 Self: Sized, {
399 Self::new(move || f().evaluate().cloned().map_err(Clone::clone))
400 }
401 }
402
403 pub type RcTryLazy<'a, A, E> = TryLazy<'a, A, E, RcLazyConfig>;
405
406 pub type ArcTryLazy<'a, A, E> = TryLazy<'a, A, E, ArcLazyConfig>;
408}
409pub use inner::*;
410
411#[cfg(test)]
412mod tests {
413 use {
414 super::*,
415 crate::types::{
416 RcLazy,
417 TryThunk,
418 TryTrampoline,
419 },
420 std::{
421 cell::RefCell,
422 rc::Rc,
423 },
424 };
425
426 #[test]
430 fn test_try_memo_caching_ok() {
431 let counter = Rc::new(RefCell::new(0));
432 let counter_clone = counter.clone();
433 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
434 *counter_clone.borrow_mut() += 1;
435 Ok(42)
436 });
437
438 assert_eq!(*counter.borrow(), 0);
439 assert_eq!(memo.evaluate(), Ok(&42));
440 assert_eq!(*counter.borrow(), 1);
441 assert_eq!(memo.evaluate(), Ok(&42));
442 assert_eq!(*counter.borrow(), 1);
443 }
444
445 #[test]
449 fn test_try_memo_caching_err() {
450 let counter = Rc::new(RefCell::new(0));
451 let counter_clone = counter.clone();
452 let memo: RcTryLazy<i32, i32> = RcTryLazy::new(move || {
453 *counter_clone.borrow_mut() += 1;
454 Err(0)
455 });
456
457 assert_eq!(*counter.borrow(), 0);
458 assert_eq!(memo.evaluate(), Err(&0));
459 assert_eq!(*counter.borrow(), 1);
460 assert_eq!(memo.evaluate(), Err(&0));
461 assert_eq!(*counter.borrow(), 1);
462 }
463
464 #[test]
468 fn test_try_memo_sharing() {
469 let counter = Rc::new(RefCell::new(0));
470 let counter_clone = counter.clone();
471 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
472 *counter_clone.borrow_mut() += 1;
473 Ok(42)
474 });
475 let shared = memo.clone();
476
477 assert_eq!(memo.evaluate(), Ok(&42));
478 assert_eq!(*counter.borrow(), 1);
479 assert_eq!(shared.evaluate(), Ok(&42));
480 assert_eq!(*counter.borrow(), 1);
481 }
482
483 #[test]
487 fn test_catch_unwind() {
488 let memo = RcTryLazy::catch_unwind(|| {
489 if true {
490 panic!("oops")
491 }
492 42
493 });
494
495 match memo.evaluate() {
496 Err(e) => assert_eq!(e, "oops"),
497 Ok(_) => panic!("Should have failed"),
498 }
499 }
500
501 #[test]
503 fn test_try_memo_from_try_eval() {
504 let eval = TryThunk::new(|| Ok::<i32, ()>(42));
505 let memo = RcTryLazy::from(eval);
506 assert_eq!(memo.evaluate(), Ok(&42));
507 }
508
509 #[test]
511 fn test_try_memo_from_try_task() {
512 let task = TryTrampoline::<i32, ()>::ok(42);
513 let memo = RcTryLazy::from(task);
514 assert_eq!(memo.evaluate(), Ok(&42));
515 }
516
517 #[test]
519 fn test_try_memo_from_rc_memo() {
520 let memo = RcLazy::new(|| 42);
521 let try_memo: crate::types::RcTryLazy<i32, ()> = crate::types::RcTryLazy::from(memo);
522 assert_eq!(try_memo.evaluate(), Ok(&42));
523 }
524
525 #[test]
527 fn test_try_memo_from_arc_memo() {
528 use crate::types::ArcLazy;
529 let memo = ArcLazy::new(|| 42);
530 let try_memo: crate::types::ArcTryLazy<i32, ()> = crate::types::ArcTryLazy::from(memo);
531 assert_eq!(try_memo.evaluate(), Ok(&42));
532 }
533
534 #[test]
536 fn test_send_defer() {
537 use crate::classes::send_deferrable::send_defer;
538
539 let memo: ArcTryLazy<i32, ()> = send_defer(|| ArcTryLazy::new(|| Ok(42)));
540 assert_eq!(memo.evaluate(), Ok(&42));
541 }
542}