#![allow(clippy::needless_doctest_main)]
#![allow(deprecated)]
use std::panic::PanicInfo as PanicHookInfo;
use std::sync::atomic::{AtomicBool, Ordering};
use std::io::Write;
pub fn install() {
std::panic::set_hook(Box::new(panic_hook));
}
pub fn panic_hook(info: &PanicHookInfo<'_>) {
let backtrace = std::backtrace::Backtrace::capture();
let location = info.location();
let msg = payload_as_str(info);
let current_thread = std::thread::current();
let thread_name = current_thread.name().unwrap_or("<unnamed>");
let mut stderr = std::io::stderr().lock();
if let Some(location) = location {
writeln!(stderr, "\nthread '{thread_name}' panicked at {location}").ok();
} else {
writeln!(stderr, "\nthread '{thread_name}' panicked").ok();
}
if let Some(msg) = msg {
writeln!(stderr, "{msg}").ok();
}
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
match backtrace.status() {
std::backtrace::BacktraceStatus::Captured => {
if std::env::var_os("RUST_BACKTRACE").is_some_and(|x| x == "full") {
writeln!(&mut stderr, "stack backtrace:\n{backtrace:#}").ok();
} else {
writeln!(&mut stderr, "stack backtrace:\n{backtrace}").ok();
}
}
std::backtrace::BacktraceStatus::Disabled => {
if FIRST_PANIC.swap(false, Ordering::Relaxed) {
writeln!(
&mut stderr,
"note: run with `RUST_BACKTRACE=1` environment variable to display a \
backtrace"
).ok();
if cfg!(miri) {
writeln!(
&mut stderr,
"note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \
for the environment variable to have an effect"
).ok();
}
}
}
std::backtrace::BacktraceStatus::Unsupported => (),
_ => (),
}
}
pub fn payload_as_str<'a>(info: &'a PanicHookInfo<'a>) -> Option<&'a str> {
if let Some(s) = info.payload().downcast_ref::<&str>() {
Some(s)
} else if let Some(s) = info.payload().downcast_ref::<String>() {
Some(s)
} else {
None
}
}