use std::any::Any;
use std::cell::RefCell;
use std::panic;
use crate::error::{Location, PanicInfo};
thread_local! {
static PANIC_INFO: RefCell<Option<PanicInfo>> = RefCell::new(None);
}
#[derive(Copy, Clone)]
pub enum BacktraceCapture {
No,
#[cfg(feature = "backtrace")]
Resolved,
#[cfg(feature = "backtrace")]
Unresolved,
}
pub fn reset_panic_info() {
PANIC_INFO.with(|pi| {
*pi.borrow_mut() = None;
});
}
pub fn take_panic(panic: &dyn Any) -> PanicInfo {
PANIC_INFO
.with(|pi| pi.borrow_mut().take())
.unwrap_or_else(move || serialize_panic(panic))
}
pub fn panic_handler(info: &panic::PanicInfo<'_>, capture_backtraces: BacktraceCapture) {
PANIC_INFO.with(|pi| {
#[allow(unused_mut)]
let mut panic = serialize_panic(info.payload());
match capture_backtraces {
BacktraceCapture::No => {}
#[cfg(feature = "backtrace")]
BacktraceCapture::Resolved => {
panic.backtrace = Some(backtrace::Backtrace::new());
}
#[cfg(feature = "backtrace")]
BacktraceCapture::Unresolved => {
panic.backtrace = Some(backtrace::Backtrace::new_unresolved());
}
}
panic.location = info.location().map(Location::from_std);
*pi.borrow_mut() = Some(panic);
});
}
pub fn init_panic_hook(capture_backtraces: BacktraceCapture) {
let next = panic::take_hook();
panic::set_hook(Box::new(move |info| {
panic_handler(info, capture_backtraces);
next(info);
}));
}
fn serialize_panic(panic: &dyn Any) -> PanicInfo {
PanicInfo::new(match panic.downcast_ref::<&'static str>() {
Some(s) => s,
None => match panic.downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
},
})
}