1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Useful utilities to make development of browser-targeted Rust applications
//! slightly less painful.

#[cfg(feature = "spawn")]
pub mod spawn;
#[cfg(feature = "spawn")]
pub use spawn::spawn;

#[cfg(feature = "sleep")]
pub mod sleep;
#[cfg(feature = "sleep")]
pub use sleep::sleep;

#[cfg(feature = "queue")]
pub mod queue;
#[cfg(feature = "queue")]
pub use queue::Queue;

#[cfg(feature = "event")]
pub mod event;

use std::fmt::Display;

use wasm_bindgen::prelude::*;
use web_sys::{Document, HtmlElement, Window};

/// Sets a panic hook that forwards panic messages to
/// [`console.error`](https://developer.mozilla.org/en-US/docs/Web/API/Console/error).
#[cfg(feature = "panic_hook")]
pub fn set_panic_hook() {
    console_error_panic_hook::set_once();
}

#[wasm_bindgen]
extern "C" {
    /// Outputs a message to the web console.
    #[wasm_bindgen(js_namespace = console)]
    pub fn log(s: &str);

    /// Outputs a warning message to the web console.
    #[wasm_bindgen(js_namespace = console)]
    pub fn warn(s: &str);

    /// Outputs an error message to the web console.
    #[wasm_bindgen(js_namespace = console)]
    pub fn error(s: &str);
}

/// Macro for [`log`] to add arguments support (like in [`print`] macro).
#[macro_export]
macro_rules! console_log {
    ($($t:tt)*) => {
        {
            use $crate::log;
            (log(&format_args!($($t)*).to_string()))
        }
    }
}

/// Macro for [`warn`] to add arguments support (like in [`print`] macro).
#[macro_export]
macro_rules! console_warn {
    ($($t:tt)*) => {
        {
            use $crate::warn;
            (warn(&format_args!($($t)*).to_string()))
        }
    }
}

/// Macro for [`error`] to add arguments support (like in [`print`] macro).
#[macro_export]
macro_rules! console_error {
    ($($t:tt)*) => {
        {
            use $crate::error;
            (error(&format_args!($($t)*).to_string()))
        }
    }
}

/// Helper macro for creating [`mod@wasm_bindgen`] closures.
#[macro_export]
macro_rules! closure {
    ($expression:expr) => {{
        use wasm_bindgen::prelude::Closure;
        Closure::wrap(Box::new($expression) as Box<dyn FnMut(_)>)
    }};
}

/// Gets window object.
///
/// This function panics when window doesn't exist.
pub fn window() -> Window {
    web_sys::window().expect("no global window exists")
}

/// Gets document object.
///
/// This function panics when document doesn't exist in window or
/// if window doesn't exist.
pub fn document() -> Document {
    window()
        .document()
        .expect("should have a document on window")
}

/// Gets document's body.
///
/// This function panics when body doesn't exist in document or
/// if document doesn't exist in window or
/// if window doesn't exist.
pub fn body() -> HtmlElement {
    document().body().expect("document should have a body")
}

/// Wrapper for [`JsValue`] errors implementing [`std::error::Error`].
#[derive(Debug)]
pub struct JsError(pub JsValue);

impl Display for JsError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self.0)
    }
}

impl std::error::Error for JsError {}

impl From<JsValue> for JsError {
    fn from(value: JsValue) -> Self {
        JsError(value)
    }
}

#[cfg(test)]
mod tests {
    use crate::{body, document, window};
    use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};

    wasm_bindgen_test_configure!(run_in_browser);

    #[wasm_bindgen_test]
    fn test_window() {
        window();
    }

    #[wasm_bindgen_test]
    fn test_body() {
        body();
    }

    #[wasm_bindgen_test]
    fn test_document() {
        document();
    }
}