browser_panic_hook/
lib.rs

1mod mode;
2mod utils;
3
4pub use mode::*;
5pub use utils::{escape_text, Unescaped};
6
7use std::panic;
8use std::sync::Once;
9
10fn handle<M>(mode: &M, info: &panic::PanicInfo)
11where
12    M: PresentationMode,
13{
14    // always print out an error on the console first
15    let msg = format!("Application panicked: {info}");
16    web_sys::console::error_1(&msg.into());
17
18    let details = PanicDetails::from(info);
19    if let Err(err) = mode.present(details) {
20        // or fail handling, showing this on the console too
21        web_sys::console::error_2(&"Panic hook failed".into(), &err.as_ref().into());
22    }
23}
24
25/// Turn something into a panic hook.
26pub trait IntoPanicHook {
27    fn into_panic_hook(self) -> Box<dyn Fn(&panic::PanicInfo) + 'static + Sync + Send>;
28}
29
30impl<T> IntoPanicHook for T
31where
32    T: PresentationMode + 'static + Send + Sync,
33{
34    fn into_panic_hook(self) -> Box<dyn Fn(&panic::PanicInfo) + 'static + Sync + Send> {
35        Box::new(move |info| handle(&self, info))
36    }
37}
38
39static SET_HOOK: Once = Once::new();
40
41/// Set the panic hook and ensure it is only set once.
42pub fn set_once<F, M>(f: F)
43where
44    F: FnOnce() -> M,
45    M: PresentationMode + 'static + Send + Sync,
46{
47    SET_HOOK.call_once(move || panic::set_hook(f().into_panic_hook()))
48}
49
50/// Set the panic hook to [`Basic`] and ensure it is only set once.
51#[inline]
52pub fn set_once_default() {
53    set_once(|| Basic)
54}