tuwunel_core/error/panic.rs
1use std::{
2 any::Any,
3 panic::{RefUnwindSafe, UnwindSafe, panic_any},
4};
5
6use super::Error;
7use crate::debug;
8
9impl UnwindSafe for Error {}
10impl RefUnwindSafe for Error {}
11
12impl Error {
13 /// Starts a panic using the boxed value derived from this error.
14 ///
15 /// Explicit panic variants and task joins that ended by panicking supply
16 /// their stored boxed value. Other errors are boxed as typed values.
17 ///
18 /// # Panics
19 ///
20 /// Always panics by design.
21 #[inline]
22 pub fn panic(self) -> ! { panic_any(self.into_panic()) }
23
24 /// Wraps a caught panic payload as an error.
25 ///
26 /// A static string message is extracted when the payload exposes one. The
27 /// original payload remains available for later unwinding.
28 #[must_use]
29 #[inline]
30 pub fn from_panic(e: Box<dyn Any + Send + 'static>) -> Self {
31 Self::Panic(debug::panic_str(&e), e.into())
32 }
33
34 /// Converts this error into a boxed value for panicking.
35 ///
36 /// Explicit panic variants and task joins that ended by panicking yield
37 /// their stored boxed value. Other variants are boxed as typed values.
38 ///
39 /// # Panics
40 ///
41 /// Panics if a stored panic payload's mutex is poisoned or a task join was
42 /// cancelled instead of ending with a panic.
43 #[inline]
44 pub fn into_panic(self) -> Box<dyn Any + Send> {
45 match self {
46 | Self::JoinError(e) => e.into_panic(),
47 | Self::Panic(_, e) | Self::PanicAny(e) =>
48 e.into_inner().expect("Error contained panic"),
49 | _ => Box::new(self),
50 }
51 }
52
53 /// Extracts a static message from a carried panic payload.
54 ///
55 /// Non-panic errors return `None`. Unsupported payload representations
56 /// produce an empty string, and the error is consumed while inspecting the
57 /// payload.
58 ///
59 /// # Panics
60 ///
61 /// Panics if a stored panic payload's mutex is poisoned.
62 #[inline]
63 pub fn panic_str(self) -> Option<&'static str> {
64 self.is_panic().then(|| {
65 let panic = self.into_panic();
66 debug::panic_str(&panic)
67 })
68 }
69
70 /// Tests whether this error carries a panic payload.
71 ///
72 /// Explicit panic variants always match. A task-join error matches only
73 /// when the joined task ended by panicking.
74 #[inline]
75 pub fn is_panic(&self) -> bool {
76 match &self {
77 | Self::JoinError(e) => e.is_panic(),
78 | Self::Panic(..) | Self::PanicAny(..) => true,
79 | _ => false,
80 }
81 }
82}