js_utils/
lib.rs

1//! Useful utilities to make development of browser-targeted Rust applications
2//! slightly less painful.
3
4#[cfg(feature = "spawn")]
5pub mod spawn;
6#[cfg(feature = "spawn")]
7pub use spawn::spawn;
8
9#[cfg(feature = "sleep")]
10pub mod sleep;
11#[cfg(feature = "sleep")]
12pub use sleep::sleep;
13
14#[cfg(feature = "queue")]
15pub mod queue;
16#[cfg(feature = "queue")]
17pub use queue::Queue;
18
19#[cfg(feature = "event")]
20pub mod event;
21
22use std::fmt::Display;
23
24use wasm_bindgen::prelude::*;
25use web_sys::{Document, HtmlElement, Window};
26
27/// Sets a panic hook that forwards panic messages to
28/// [`console.error`](https://developer.mozilla.org/en-US/docs/Web/API/Console/error).
29#[cfg(feature = "panic_hook")]
30pub fn set_panic_hook() {
31    console_error_panic_hook::set_once();
32}
33
34#[wasm_bindgen]
35extern "C" {
36    /// Outputs a message to the web console.
37    #[wasm_bindgen(js_namespace = console)]
38    pub fn log(s: &str);
39
40    /// Outputs a warning message to the web console.
41    #[wasm_bindgen(js_namespace = console)]
42    pub fn warn(s: &str);
43
44    /// Outputs an error message to the web console.
45    #[wasm_bindgen(js_namespace = console)]
46    pub fn error(s: &str);
47}
48
49/// Macro for [`log`] to add arguments support (like in [`print`] macro).
50#[macro_export]
51macro_rules! console_log {
52    ($($t:tt)*) => {
53        {
54            ($crate::log(&format_args!($($t)*).to_string()))
55        }
56    }
57}
58
59/// Macro for [`warn`] to add arguments support (like in [`print`] macro).
60#[macro_export]
61macro_rules! console_warn {
62    ($($t:tt)*) => {
63        {
64            ($crate::warn(&format_args!($($t)*).to_string()))
65        }
66    }
67}
68
69/// Macro for [`error`] to add arguments support (like in [`print`] macro).
70#[macro_export]
71macro_rules! console_error {
72    ($($t:tt)*) => {
73        {
74            ($crate::error(&format_args!($($t)*).to_string()))
75        }
76    }
77}
78
79/// Helper macro for creating [`mod@wasm_bindgen`] closures.
80#[macro_export]
81macro_rules! closure {
82    ($expression:expr) => {{
83        wasm_bindgen::prelude::Closure::wrap(Box::new($expression) as Box<dyn FnMut(_)>)
84    }};
85}
86
87/// Gets window object.
88///
89/// This function panics when window doesn't exist.
90pub fn window() -> Window {
91    web_sys::window().expect("no global window exists")
92}
93
94/// Gets document object.
95///
96/// This function panics when document doesn't exist in window or
97/// if window doesn't exist.
98pub fn document() -> Document {
99    window()
100        .document()
101        .expect("should have a document on window")
102}
103
104/// Gets document's body.
105///
106/// This function panics when body doesn't exist in document or
107/// if document doesn't exist in window or
108/// if window doesn't exist.
109pub fn body() -> HtmlElement {
110    document().body().expect("document should have a body")
111}
112
113/// Wrapper for [`JsValue`] errors implementing [`std::error::Error`].
114#[derive(Debug)]
115pub struct JsError(pub JsValue);
116
117impl Display for JsError {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        write!(f, "{:?}", self.0)
120    }
121}
122
123impl std::error::Error for JsError {}
124
125impl From<JsValue> for JsError {
126    fn from(value: JsValue) -> Self {
127        JsError(value)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use crate::{body, document, window};
134    use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
135
136    wasm_bindgen_test_configure!(run_in_browser);
137
138    #[wasm_bindgen_test]
139    fn test_window() {
140        window();
141    }
142
143    #[wasm_bindgen_test]
144    fn test_body() {
145        body();
146    }
147
148    #[wasm_bindgen_test]
149    fn test_document() {
150        document();
151    }
152}