1use crate::{backtrace::Backtrace, context::Context};
2use std::{
3 any::Any,
4 fmt,
5 panic::{self, UnwindSafe},
6};
7
8#[inline]
14pub fn maybe_unwind<F, R>(f: F) -> Result<R, Unwind>
15where
16 F: FnOnce() -> R + UnwindSafe,
17{
18 let mut captured: Option<Captured> = None;
19
20 let mut ctx = Context {
21 captured: &mut captured,
22 };
23
24 let res = with_set_ctx!(&mut ctx, { panic::catch_unwind(f) });
25
26 res.map_err(|payload| Unwind {
27 payload,
28 captured: captured.take(),
29 })
30}
31
32#[derive(Debug)]
34pub struct Unwind {
35 payload: Box<dyn Any + Send + 'static>,
36 captured: Option<Captured>,
37}
38
39#[derive(Debug)]
40pub(crate) struct Captured {
41 pub(crate) location: Option<Location>,
42 pub(crate) backtrace: Option<Backtrace>,
43}
44
45impl Unwind {
46 #[inline]
48 pub fn payload(&self) -> &(dyn Any + Send + 'static) {
49 &*self.payload
50 }
51
52 #[inline]
54 pub fn payload_str(&self) -> &str {
55 let payload = self.payload();
56 (payload.downcast_ref::<&str>().copied())
57 .or_else(|| payload.downcast_ref::<String>().map(|s| s.as_str()))
58 .unwrap_or_else(|| "Box<dyn Any>")
59 }
60
61 #[inline]
63 pub fn into_payload(self) -> Box<dyn Any + Send + 'static> {
64 self.payload
65 }
66
67 #[inline]
69 pub fn location(&self) -> Option<&Location> {
70 self.captured.as_ref()?.location.as_ref()
71 }
72
73 #[cfg(backtrace)]
77 #[inline]
78 pub fn backtrace(&self) -> Option<&Backtrace> {
79 self.captured.as_ref()?.backtrace.as_ref()
80 }
81}
82
83impl fmt::Display for Unwind {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 let msg = self.payload_str();
86 if !f.alternate() {
87 return f.write_str(msg);
88 }
89
90 if let Some(location) = self.location() {
91 writeln!(f, "panicked at {}: {}", location, msg)?;
92 } else {
93 writeln!(f, "panicked: {}", msg)?;
94 }
95
96 #[cfg(backtrace)]
97 {
98 use std::backtrace::BacktraceStatus;
99
100 if let Some(backtrace) = self.backtrace() {
101 if let BacktraceStatus::Captured = backtrace.status() {
102 writeln!(f, "stack backtrace:")?;
103 writeln!(f, "{}", backtrace)?;
104 }
105 }
106 }
107
108 Ok(())
109 }
110}
111
112#[derive(Debug)]
114pub struct Location {
115 file: String,
116 line: u32,
117 column: u32,
118}
119
120impl Location {
121 #[inline]
122 pub(crate) fn from_std(loc: &panic::Location<'_>) -> Self {
123 Self {
124 file: loc.file().to_string(),
125 line: loc.line(),
126 column: loc.column(),
127 }
128 }
129
130 #[inline]
132 pub fn file(&self) -> &str {
133 self.file.as_str()
134 }
135
136 #[inline]
138 pub fn line(&self) -> u32 {
139 self.line
140 }
141
142 #[inline]
144 pub fn column(&self) -> u32 {
145 self.column
146 }
147}
148
149impl fmt::Display for Location {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(f, "{}:{}:{}", self.file, self.line, self.column)
152 }
153}