1use crate::{
6 brands::TryLazyBrand,
7 classes::{Deferrable, SendDeferrable},
8 impl_kind,
9 kinds::*,
10 types::{ArcLazyConfig, Lazy, LazyConfig, RcLazyConfig, TryThunk, TryTrampoline},
11};
12use fp_macros::{doc_params, doc_type_params, hm_signature};
13
14pub struct TryLazy<'a, A, E, Config: LazyConfig = RcLazyConfig>(
29 pub(crate) Config::TryLazy<'a, A, E>,
30)
31where
32 A: 'a,
33 E: 'a;
34
35impl<'a, A, E, Config: LazyConfig> Clone for TryLazy<'a, A, E, Config>
36where
37 A: 'a,
38 E: 'a,
39{
40 fn clone(&self) -> Self {
41 Self(self.0.clone())
42 }
43}
44
45impl<'a, A, E, Config: LazyConfig> TryLazy<'a, A, E, Config>
46where
47 A: 'a,
48 E: 'a,
49{
50 #[hm_signature]
55 pub fn evaluate(&self) -> Result<&A, &E> {
69 Config::try_evaluate(&self.0)
70 }
71}
72
73impl<'a, A, E> TryLazy<'a, A, E, RcLazyConfig>
74where
75 A: 'a,
76 E: 'a,
77{
78 #[hm_signature]
83 #[doc_type_params("The type of the initializer closure.")]
87 #[doc_params("The closure that produces the result.")]
91 pub fn new<F>(f: F) -> Self
105 where
106 F: FnOnce() -> Result<A, E> + 'a,
107 {
108 TryLazy(RcLazyConfig::try_lazy_new(Box::new(f)))
109 }
110}
111
112impl<'a, A, E> From<TryThunk<'a, A, E>> for TryLazy<'a, A, E, RcLazyConfig> {
113 fn from(eval: TryThunk<'a, A, E>) -> Self {
114 Self::new(move || eval.evaluate())
115 }
116}
117
118impl<'a, A, E> From<TryTrampoline<A, E>> for TryLazy<'a, A, E, RcLazyConfig>
119where
120 A: Send,
121 E: Send,
122{
123 fn from(task: TryTrampoline<A, E>) -> Self {
124 Self::new(move || task.evaluate())
125 }
126}
127
128impl<'a, A, E> From<Lazy<'a, A, ArcLazyConfig>> for TryLazy<'a, A, E, ArcLazyConfig>
129where
130 A: Clone + Send + Sync + 'a,
131 E: Send + Sync + 'a,
132{
133 fn from(memo: Lazy<'a, A, ArcLazyConfig>) -> Self {
134 Self::new(move || Ok(memo.evaluate().clone()))
135 }
136}
137
138impl<'a, A, E> From<Lazy<'a, A, RcLazyConfig>> for TryLazy<'a, A, E, RcLazyConfig>
139where
140 A: Clone + 'a,
141 E: 'a,
142{
143 fn from(memo: Lazy<'a, A, RcLazyConfig>) -> Self {
144 Self::new(move || Ok(memo.evaluate().clone()))
145 }
146}
147
148impl<'a, A> TryLazy<'a, A, String, RcLazyConfig>
149where
150 A: 'a,
151{
152 #[hm_signature]
157 #[doc_type_params("The type of the initializer closure.")]
161 #[doc_params("The closure that might panic.")]
165 pub fn catch_unwind<F>(f: F) -> Self
182 where
183 F: FnOnce() -> A + std::panic::UnwindSafe + 'a,
184 {
185 Self::new(move || {
186 std::panic::catch_unwind(f).map_err(|e| {
187 if let Some(s) = e.downcast_ref::<&str>() {
188 s.to_string()
189 } else if let Some(s) = e.downcast_ref::<String>() {
190 s.clone()
191 } else {
192 "Unknown panic".to_string()
193 }
194 })
195 })
196 }
197}
198
199impl<'a, A, E> TryLazy<'a, A, E, ArcLazyConfig>
200where
201 A: 'a,
202 E: 'a,
203{
204 #[hm_signature]
209 #[doc_type_params("The type of the initializer closure.")]
213 #[doc_params("The closure that produces the result.")]
217 pub fn new<F>(f: F) -> Self
231 where
232 F: FnOnce() -> Result<A, E> + Send + 'a,
233 {
234 TryLazy(ArcLazyConfig::try_lazy_new(Box::new(f)))
235 }
236}
237
238impl<'a, A, E> Deferrable<'a> for TryLazy<'a, A, E, RcLazyConfig>
239where
240 A: Clone + 'a,
241 E: Clone + 'a,
242{
243 #[hm_signature(Deferrable)]
251 #[doc_type_params("The type of the thunk.")]
255 #[doc_params("The thunk that produces the lazy value.")]
259 fn defer<F>(f: F) -> Self
273 where
274 F: FnOnce() -> Self + 'a,
275 Self: Sized,
276 {
277 Self::new(move || f().evaluate().cloned().map_err(Clone::clone))
278 }
279}
280
281impl_kind! {
282 impl<E: 'static, Config: LazyConfig> for TryLazyBrand<E, Config> {
283 type Of<'a, A: 'a>: 'a = TryLazy<'a, A, E, Config>;
284 }
285}
286
287impl<'a, A, E> SendDeferrable<'a> for TryLazy<'a, A, E, ArcLazyConfig>
288where
289 A: Clone + Send + Sync + 'a,
290 E: Clone + Send + Sync + 'a,
291{
292 #[hm_signature(SendDeferrable)]
300 #[doc_type_params("The type of the thunk.")]
304 #[doc_params("The thunk that produces the lazy value.")]
308 fn send_defer<F>(f: F) -> Self
322 where
323 F: FnOnce() -> Self + Send + Sync + 'a,
324 Self: Sized,
325 {
326 Self::new(move || f().evaluate().cloned().map_err(Clone::clone))
327 }
328}
329
330pub type RcTryLazy<'a, A, E> = TryLazy<'a, A, E, RcLazyConfig>;
332
333pub type ArcTryLazy<'a, A, E> = TryLazy<'a, A, E, ArcLazyConfig>;
335
336#[cfg(test)]
337mod tests {
338 use crate::types::RcLazy;
339
340 use super::*;
341 use std::cell::RefCell;
342 use std::rc::Rc;
343
344 #[test]
348 fn test_try_memo_caching_ok() {
349 let counter = Rc::new(RefCell::new(0));
350 let counter_clone = counter.clone();
351 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
352 *counter_clone.borrow_mut() += 1;
353 Ok(42)
354 });
355
356 assert_eq!(*counter.borrow(), 0);
357 assert_eq!(memo.evaluate(), Ok(&42));
358 assert_eq!(*counter.borrow(), 1);
359 assert_eq!(memo.evaluate(), Ok(&42));
360 assert_eq!(*counter.borrow(), 1);
361 }
362
363 #[test]
367 fn test_try_memo_caching_err() {
368 let counter = Rc::new(RefCell::new(0));
369 let counter_clone = counter.clone();
370 let memo: RcTryLazy<i32, i32> = RcTryLazy::new(move || {
371 *counter_clone.borrow_mut() += 1;
372 Err(0)
373 });
374
375 assert_eq!(*counter.borrow(), 0);
376 assert_eq!(memo.evaluate(), Err(&0));
377 assert_eq!(*counter.borrow(), 1);
378 assert_eq!(memo.evaluate(), Err(&0));
379 assert_eq!(*counter.borrow(), 1);
380 }
381
382 #[test]
386 fn test_try_memo_sharing() {
387 let counter = Rc::new(RefCell::new(0));
388 let counter_clone = counter.clone();
389 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
390 *counter_clone.borrow_mut() += 1;
391 Ok(42)
392 });
393 let shared = memo.clone();
394
395 assert_eq!(memo.evaluate(), Ok(&42));
396 assert_eq!(*counter.borrow(), 1);
397 assert_eq!(shared.evaluate(), Ok(&42));
398 assert_eq!(*counter.borrow(), 1);
399 }
400
401 #[test]
405 fn test_catch_unwind() {
406 let memo = RcTryLazy::catch_unwind(|| {
407 if true {
408 panic!("oops")
409 }
410 42
411 });
412
413 match memo.evaluate() {
414 Err(e) => assert_eq!(e, "oops"),
415 Ok(_) => panic!("Should have failed"),
416 }
417 }
418
419 #[test]
421 fn test_try_memo_from_try_eval() {
422 let eval = TryThunk::new(|| Ok::<i32, ()>(42));
423 let memo = RcTryLazy::from(eval);
424 assert_eq!(memo.evaluate(), Ok(&42));
425 }
426
427 #[test]
429 fn test_try_memo_from_try_task() {
430 let task = TryTrampoline::<i32, ()>::ok(42);
431 let memo = RcTryLazy::from(task);
432 assert_eq!(memo.evaluate(), Ok(&42));
433 }
434
435 #[test]
437 fn test_try_memo_from_rc_memo() {
438 let memo = RcLazy::new(|| 42);
439 let try_memo: crate::types::RcTryLazy<i32, ()> = crate::types::RcTryLazy::from(memo);
440 assert_eq!(try_memo.evaluate(), Ok(&42));
441 }
442
443 #[test]
445 fn test_try_memo_from_arc_memo() {
446 use crate::types::ArcLazy;
447 let memo = ArcLazy::new(|| 42);
448 let try_memo: crate::types::ArcTryLazy<i32, ()> = crate::types::ArcTryLazy::from(memo);
449 assert_eq!(try_memo.evaluate(), Ok(&42));
450 }
451
452 #[test]
454 fn test_send_defer() {
455 use crate::classes::send_deferrable::send_defer;
456
457 let memo: ArcTryLazy<i32, ()> = send_defer(|| ArcTryLazy::new(|| Ok(42)));
458 assert_eq!(memo.evaluate(), Ok(&42));
459 }
460}