Skip to main content

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}