1use std::{
5 any::Any,
6 mem,
7 panic::{catch_unwind, resume_unwind, AssertUnwindSafe, UnwindSafe},
8 process::abort,
9};
10
11pub struct Payload(Option<Box<dyn Any + Send + 'static>>);
13
14impl Payload {
15 #[inline]
17 pub fn get(&self) -> &(dyn Any + Send + 'static) {
18 let Some(payload) = &self.0 else {
19 unreachable!()
20 };
21 payload
22 }
23
24 #[inline]
26 pub fn get_mut(&mut self) -> &mut (dyn Any + Send + 'static) {
27 let Some(payload) = &mut self.0 else {
28 unreachable!()
29 };
30 payload
31 }
32
33 #[inline]
35 pub fn into_inner(mut self) -> Box<dyn Any + Send + 'static> {
36 self.0.take().unwrap()
37 }
38
39 #[inline]
41 pub fn drop_or_abort(self) {
42 drop_or_abort(self.into_inner())
43 }
44
45 #[inline]
47 pub fn drop_or_forget(self) {
48 drop_or_forget(self.into_inner())
49 }
50
51 #[inline]
53 pub fn resume_unwind(self) {
54 resume_unwind(self.into_inner())
55 }
56}
57
58impl Drop for Payload {
59 #[inline]
60 fn drop(&mut self) {
61 if let Some(payload) = self.0.take() {
62 drop_or_abort(payload)
63 }
64 }
65}
66
67#[inline]
74#[must_use]
75pub fn catch_unwind_or_abort<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Option<R> {
76 match catch_unwind(f) {
77 Ok(ok) => Some(ok),
78 Err(err) => {
79 drop_or_abort(err);
80 None
81 }
82 }
83}
84
85#[inline]
92#[must_use]
93pub fn catch_unwind_or_forget<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Option<R> {
94 match catch_unwind(f) {
95 Ok(ok) => Some(ok),
96 Err(err) => {
97 drop_or_forget(err);
98 None
99 }
100 }
101}
102
103#[inline]
111pub fn catch_unwind_wrapped<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R, Payload> {
112 catch_unwind(f).map_err(|e| Payload(Some(e)))
113}
114
115#[inline]
118pub fn drop_or_else<T, F: FnOnce(Box<dyn Any + Send + 'static>) -> E, E>(
119 value: T,
120 or_else: F,
121) -> Result<(), E> {
122 catch_unwind(AssertUnwindSafe(move || mem::drop(value))).map_err(or_else)
123}
124
125#[inline]
127pub fn drop_or_abort<T>(value: T) {
128 let _ = drop_or_else(value, |_err| abort());
129}
130
131#[inline]
133pub fn drop_or_forget<T>(value: T) {
134 let _ = drop_or_else(value, mem::forget);
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use std::panic::panic_any;
141
142 fn endless_panic() {
143 struct PanicOnDrop;
144
145 impl Drop for PanicOnDrop {
146 fn drop(&mut self) {
147 panic_any(Self)
148 }
149 }
150
151 panic_any(PanicOnDrop)
152 }
153
154 #[test]
155 fn test_catch_unwind_or_forget() {
156 assert_eq!(catch_unwind_or_forget(|| "success"), Some("success"));
157 assert_eq!(catch_unwind_or_forget(endless_panic), None);
158 }
159
160 #[test]
161 fn test_catch_unwind_wrapped() {
162 assert!(matches!(catch_unwind_wrapped(|| "success"), Ok("success")));
163
164 match catch_unwind(|| match catch_unwind_wrapped(endless_panic) {
165 Ok(()) => unreachable!(),
166 Err(payload) => payload.drop_or_forget(),
167 }) {
168 Ok(()) => (),
169 Err(_) => panic!("Payload::drop_or_forget didn't forget"),
170 }
171
172 match catch_unwind(|| match catch_unwind_wrapped(endless_panic) {
173 Ok(()) => unreachable!(),
174 Err(payload) => payload.resume_unwind(),
175 }) {
176 Ok(()) => panic!("Payload::resume_unwind didn't resume"),
177 Err(err) => drop_or_forget(err),
178 }
179 }
180}