1use super::boxed::BoxedFnOnce;
3use derive_more::Debug;
4use std::any::Any;
5use std::fmt;
6use std::panic::AssertUnwindSafe;
7
8pub type PanicPayload = Box<dyn Any + Send + 'static>;
9
10#[derive(Debug)]
12pub struct Panic<T: Send + fmt::Debug + Eq> {
13 #[debug("<panic! arg>")]
14 payload: PanicPayload,
15 detail: Option<T>,
16}
17
18impl<T: Send + fmt::Debug + Eq> Panic<T> {
19 pub fn try_call<O, F: FnOnce() -> O>(detail: Option<T>, f: F) -> Result<O, Self> {
22 std::panic::catch_unwind(AssertUnwindSafe(f)).map_err(|payload| Self { payload, detail })
23 }
24
25 pub(crate) fn try_call_boxed<O, F: BoxedFnOnce<Output = O> + ?Sized>(
26 detail: Option<T>,
27 f: Box<F>,
28 ) -> Result<O, Self> {
29 std::panic::catch_unwind(AssertUnwindSafe(|| f.call_box()))
30 .map_err(|payload| Self { payload, detail })
31 }
32
33 pub fn payload(&self) -> &PanicPayload {
35 &self.payload
36 }
37
38 pub fn detail(&self) -> Option<&T> {
40 self.detail.as_ref()
41 }
42
43 pub fn resume(self) -> ! {
45 std::panic::resume_unwind(self.payload)
46 }
47}
48
49impl<T: Send + fmt::Debug + Eq> PartialEq for Panic<T> {
50 fn eq(&self, other: &Self) -> bool {
51 (*self.payload).type_id() == (*other.payload).type_id() && self.detail == other.detail
52 }
53}
54
55impl<T: Send + fmt::Debug + Eq> Eq for Panic<T> {}
56
57#[cfg(test)]
58#[cfg_attr(coverage_nightly, coverage(off))]
59mod tests {
60 use super::Panic;
61 use std::fmt::Debug;
62
63 impl<T: Send + Debug + Eq> Panic<T> {
64 pub fn new(msg: &str, detail: Option<T>) -> Self {
66 let payload = std::panic::catch_unwind(|| panic!("{}", msg))
67 .err()
68 .unwrap();
69 Self { payload, detail }
70 }
71 }
72
73 #[test]
74 fn test_catch_panic() {
75 let result = Panic::try_call("test".into(), || panic!("panic!"));
76 let panic = result.unwrap_err();
77 assert_eq!(*panic.detail().unwrap(), "test");
78 }
79
80 #[test]
81 #[should_panic]
82 fn test_resume_panic() {
83 let result = Panic::try_call("test".into(), || panic!("panic!"));
84 let panic = result.unwrap_err();
85 panic.resume();
86 }
87}