1use crate::types::{ArcLazyConfig, Lazy, LazyConfig, RcLazyConfig, TryThunk, TryTrampoline};
2
3pub struct TryLazy<'a, A, E, Config: LazyConfig = RcLazyConfig>(
18 pub(crate) Config::TryLazy<'a, A, E>,
19)
20where
21 A: 'a,
22 E: 'a;
23
24impl<'a, A, E, Config: LazyConfig> Clone for TryLazy<'a, A, E, Config>
25where
26 A: 'a,
27 E: 'a,
28{
29 fn clone(&self) -> Self {
30 Self(self.0.clone())
31 }
32}
33
34impl<'a, A, E, Config: LazyConfig> TryLazy<'a, A, E, Config>
35where
36 A: 'a,
37 E: 'a,
38{
39 pub fn get(&self) -> Result<&A, &E> {
58 Config::force_try(&self.0)
59 }
60}
61
62impl<'a, A, E> TryLazy<'a, A, E, RcLazyConfig>
63where
64 A: 'a,
65 E: 'a,
66{
67 pub fn new<F>(f: F) -> Self
94 where
95 F: FnOnce() -> Result<A, E> + 'a,
96 {
97 TryLazy(RcLazyConfig::new_try_lazy(Box::new(f)))
98 }
99}
100
101impl<'a, A, E> From<TryThunk<'a, A, E>> for TryLazy<'a, A, E, RcLazyConfig> {
102 fn from(eval: TryThunk<'a, A, E>) -> Self {
103 Self::new(move || eval.run())
104 }
105}
106
107impl<'a, A, E> From<TryTrampoline<A, E>> for TryLazy<'a, A, E, RcLazyConfig>
108where
109 A: Send,
110 E: Send,
111{
112 fn from(task: TryTrampoline<A, E>) -> Self {
113 Self::new(move || task.run())
114 }
115}
116
117impl<'a, A, E> From<Lazy<'a, A, ArcLazyConfig>> for TryLazy<'a, A, E, ArcLazyConfig>
118where
119 A: Clone + Send + Sync + 'a,
120 E: Send + Sync + 'a,
121{
122 fn from(memo: Lazy<'a, A, ArcLazyConfig>) -> Self {
123 Self::new(move || Ok(memo.get().clone()))
124 }
125}
126
127impl<'a, A, E> From<Lazy<'a, A, RcLazyConfig>> for TryLazy<'a, A, E, RcLazyConfig>
128where
129 A: Clone + 'a,
130 E: 'a,
131{
132 fn from(memo: Lazy<'a, A, RcLazyConfig>) -> Self {
133 Self::new(move || Ok(memo.get().clone()))
134 }
135}
136
137impl<'a, A> TryLazy<'a, A, String, RcLazyConfig>
138where
139 A: 'a,
140{
141 pub fn catch_unwind<F>(f: F) -> Self
171 where
172 F: FnOnce() -> A + std::panic::UnwindSafe + 'a,
173 {
174 Self::new(move || {
175 std::panic::catch_unwind(f).map_err(|e| {
176 if let Some(s) = e.downcast_ref::<&str>() {
177 s.to_string()
178 } else if let Some(s) = e.downcast_ref::<String>() {
179 s.clone()
180 } else {
181 "Unknown panic".to_string()
182 }
183 })
184 })
185 }
186}
187
188impl<'a, A, E> TryLazy<'a, A, E, ArcLazyConfig>
189where
190 A: 'a,
191 E: 'a,
192{
193 pub fn new<F>(f: F) -> Self
220 where
221 F: FnOnce() -> Result<A, E> + Send + 'a,
222 {
223 TryLazy(ArcLazyConfig::new_try_lazy(Box::new(f)))
224 }
225}
226
227pub type RcTryLazy<'a, A, E> = TryLazy<'a, A, E, RcLazyConfig>;
229
230pub type ArcTryLazy<'a, A, E> = TryLazy<'a, A, E, ArcLazyConfig>;
232
233#[cfg(test)]
234mod tests {
235 use crate::types::RcLazy;
236
237 use super::*;
238 use std::cell::RefCell;
239 use std::rc::Rc;
240
241 #[test]
245 fn test_try_memo_caching_ok() {
246 let counter = Rc::new(RefCell::new(0));
247 let counter_clone = counter.clone();
248 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
249 *counter_clone.borrow_mut() += 1;
250 Ok(42)
251 });
252
253 assert_eq!(*counter.borrow(), 0);
254 assert_eq!(memo.get(), Ok(&42));
255 assert_eq!(*counter.borrow(), 1);
256 assert_eq!(memo.get(), Ok(&42));
257 assert_eq!(*counter.borrow(), 1);
258 }
259
260 #[test]
264 fn test_try_memo_caching_err() {
265 let counter = Rc::new(RefCell::new(0));
266 let counter_clone = counter.clone();
267 let memo: RcTryLazy<i32, i32> = RcTryLazy::new(move || {
268 *counter_clone.borrow_mut() += 1;
269 Err(0)
270 });
271
272 assert_eq!(*counter.borrow(), 0);
273 assert_eq!(memo.get(), Err(&0));
274 assert_eq!(*counter.borrow(), 1);
275 assert_eq!(memo.get(), Err(&0));
276 assert_eq!(*counter.borrow(), 1);
277 }
278
279 #[test]
283 fn test_try_memo_sharing() {
284 let counter = Rc::new(RefCell::new(0));
285 let counter_clone = counter.clone();
286 let memo: RcTryLazy<i32, ()> = RcTryLazy::new(move || {
287 *counter_clone.borrow_mut() += 1;
288 Ok(42)
289 });
290 let shared = memo.clone();
291
292 assert_eq!(memo.get(), Ok(&42));
293 assert_eq!(*counter.borrow(), 1);
294 assert_eq!(shared.get(), Ok(&42));
295 assert_eq!(*counter.borrow(), 1);
296 }
297
298 #[test]
302 fn test_catch_unwind() {
303 let memo = RcTryLazy::catch_unwind(|| {
304 if true {
305 panic!("oops")
306 }
307 42
308 });
309
310 match memo.get() {
311 Err(e) => assert_eq!(e, "oops"),
312 Ok(_) => panic!("Should have failed"),
313 }
314 }
315
316 #[test]
318 fn test_try_memo_from_try_eval() {
319 let eval = TryThunk::new(|| Ok::<i32, ()>(42));
320 let memo = RcTryLazy::from(eval);
321 assert_eq!(memo.get(), Ok(&42));
322 }
323
324 #[test]
326 fn test_try_memo_from_try_task() {
327 let task = TryTrampoline::<i32, ()>::ok(42);
328 let memo = RcTryLazy::from(task);
329 assert_eq!(memo.get(), Ok(&42));
330 }
331
332 #[test]
334 fn test_try_memo_from_rc_memo() {
335 let memo = RcLazy::new(|| 42);
336 let try_memo: crate::types::RcTryLazy<i32, ()> = crate::types::RcTryLazy::from(memo);
337 assert_eq!(try_memo.get(), Ok(&42));
338 }
339
340 #[test]
342 fn test_try_memo_from_arc_memo() {
343 use crate::types::ArcLazy;
344 let memo = ArcLazy::new(|| 42);
345 let try_memo: crate::types::ArcTryLazy<i32, ()> = crate::types::ArcTryLazy::from(memo);
346 assert_eq!(try_memo.get(), Ok(&42));
347 }
348}