use anyhow::Result;
thread_local! {
static SUPPRESS_PANIC_LOG: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
fn install_quiet_panic_hook() {
static HOOK: std::sync::OnceLock<()> = std::sync::OnceLock::new();
HOOK.get_or_init(|| {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if !SUPPRESS_PANIC_LOG.with(|s| s.get()) {
prev(info);
}
}));
});
}
pub(crate) fn catch_unwind_as_error<T>(what: &str, f: impl FnOnce() -> T) -> Result<T> {
install_quiet_panic_hook();
struct Unsuppress;
impl Drop for Unsuppress {
fn drop(&mut self) {
SUPPRESS_PANIC_LOG.with(|s| s.set(false));
}
}
SUPPRESS_PANIC_LOG.with(|s| s.set(true));
let _guard = Unsuppress;
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
Ok(v) => Ok(v),
Err(payload) => {
let msg = payload
.downcast_ref::<&str>()
.copied()
.or_else(|| payload.downcast_ref::<String>().map(String::as_str))
.unwrap_or("non-string panic payload");
Err(anyhow::anyhow!("{what} panicked: {msg}"))
}
}
}