browser_panic_hook/mode/
mod.rs

1mod basic;
2mod custom_body;
3
4pub use basic::Basic;
5pub use custom_body::CustomBody;
6
7use crate::utils::{extract_message, Unescaped};
8use std::borrow::Cow;
9use std::ops::Deref;
10use std::panic::PanicInfo;
11use wasm_bindgen::{JsCast, JsValue};
12use web_sys::{Document, HtmlElement};
13
14pub trait PresentationMode {
15    fn present(&self, info: PanicDetails) -> Result<(), Cow<'static, str>>;
16}
17
18pub struct PanicDetails<'a>(pub &'a PanicInfo<'a>);
19
20impl<'a> PanicDetails<'a> {
21    pub fn message(&self) -> Unescaped {
22        extract_message(&self.0).into()
23    }
24
25    pub fn location(&self) -> Option<Unescaped> {
26        self.0.location().map(|l| Unescaped::from(l.to_string()))
27    }
28}
29
30impl<'a> Deref for PanicDetails<'a> {
31    type Target = PanicInfo<'a>;
32
33    fn deref(&self) -> &Self::Target {
34        &self.0
35    }
36}
37
38impl<'a> From<&'a PanicInfo<'a>> for PanicDetails<'a> {
39    fn from(info: &'a PanicInfo) -> Self {
40        Self(info)
41    }
42}
43
44fn build_body(
45    doc: &Document,
46    body_class: Option<&str>,
47    content: String,
48) -> Result<HtmlElement, JsValue> {
49    let body = doc.create_element("body")?;
50
51    let body: HtmlElement = body
52        .dyn_into()
53        .map_err(|_| JsValue::from_str("Unable to convert into HTML element"))?;
54
55    if let Some(class) = body_class {
56        body.set_class_name(class);
57    }
58
59    body.set_inner_html(&content);
60
61    Ok(body)
62}
63
64fn set_body(class: Option<&str>, content: String) -> Result<(), Cow<'static, str>> {
65    let doc = web_sys::window()
66        .and_then(|w| w.document())
67        .ok_or_else(|| "Unable to acquire document")?;
68
69    doc.set_body(Some(&build_body(&doc, class, content).map_err(
70        |err| match err.as_string() {
71            Some(err) => err,
72            None => format!("{err:?}"),
73        },
74    )?));
75
76    Ok(())
77}