use {
alloc::string::String,
core::{any::Any, cell::Cell, panic::UnwindSafe},
std::{panic, sync::Once, thread_local},
};
thread_local! {
static QUIET: Cell<bool> = const { Cell::new(false) };
}
static INSTALL_HOOK: Once = Once::new();
struct Quiet {
restore: bool,
}
impl Quiet {
#[inline]
fn new() -> Self {
install_hook();
Self {
restore: QUIET.with(|quiet| quiet.replace(true)),
}
}
}
impl Drop for Quiet {
#[inline]
fn drop(&mut self) {
QUIET.with(|quiet| quiet.set(self.restore));
}
}
#[inline]
fn install_hook() {
INSTALL_HOOK.call_once(|| {
let hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
if QUIET.with(Cell::get) {
return;
}
let () = hook(info);
}));
});
}
#[inline]
fn message(panic: &(dyn Any + Send)) -> Option<String> {
panic
.downcast_ref::<&'static str>()
.map(|s| String::from(*s))
.or_else(|| panic.downcast_ref::<String>().cloned())
}
#[inline]
pub fn catch<F>(f: F) -> Result<(), Option<String>>
where
F: FnOnce() + UnwindSafe,
{
let _quiet = Quiet::new();
panic::catch_unwind(f).map_err(|panic| message(&*panic))
}