1use wasm_bindgen::prelude::*;
7use web_sys::console;
8
9pub fn set_panic_hook() {
11 console_error_panic_hook::set_once();
12}
13
14pub fn log(message: &str) {
16 console::log_1(&JsValue::from_str(message));
17}
18
19pub fn warn(message: &str) {
21 console::warn_1(&JsValue::from_str(message));
22}
23
24pub fn error(message: &str) {
26 console::error_1(&JsValue::from_str(message));
27}
28
29pub fn debug(message: &str) {
31 console::debug_1(&JsValue::from_str(message));
32}
33
34pub fn measure_time<F, R>(name: &str, f: F) -> R
36where
37 F: FnOnce() -> R,
38{
39 let start = js_sys::Date::now();
40 let result = f();
41 let elapsed = js_sys::Date::now() - start;
42 log(&format!("{} took {:.2}ms", name, elapsed));
43 result
44}
45
46#[wasm_bindgen]
48pub fn is_web_worker() -> bool {
49 js_sys::eval("typeof WorkerGlobalScope !== 'undefined'")
50 .map(|v| v.is_truthy())
51 .unwrap_or(false)
52}
53
54#[wasm_bindgen]
56pub fn is_wasm_supported() -> bool {
57 js_sys::eval("typeof WebAssembly !== 'undefined'")
58 .map(|v| v.is_truthy())
59 .unwrap_or(false)
60}
61
62#[wasm_bindgen]
64pub fn get_performance_metrics() -> Result<JsValue, JsValue> {
65 let window = web_sys::window().ok_or_else(|| JsValue::from_str("No window object"))?;
66 let performance = window
67 .performance()
68 .ok_or_else(|| JsValue::from_str("No performance object"))?;
69
70 let timing = performance.timing();
71
72 let metrics = serde_json::json!({
73 "navigation_start": timing.navigation_start(),
74 "dom_complete": timing.dom_complete(),
75 "load_event_end": timing.load_event_end(),
76 });
77
78 serde_wasm_bindgen::to_value(&metrics)
79 .map_err(|e| JsValue::from_str(&format!("Failed to serialize metrics: {}", e)))
80}
81
82#[wasm_bindgen]
84pub fn get_memory_info() -> Result<JsValue, JsValue> {
85 let window = web_sys::window().ok_or_else(|| JsValue::from_str("No window object"))?;
87 let performance = window
88 .performance()
89 .ok_or_else(|| JsValue::from_str("No performance object"))?;
90
91 let result = js_sys::Reflect::get(&performance, &JsValue::from_str("memory"));
93
94 if let Ok(memory) = result {
95 if !memory.is_undefined() {
96 return Ok(memory);
97 }
98 }
99
100 Ok(js_sys::Object::new().into())
102}
103
104pub fn format_bytes(bytes: f64) -> String {
106 const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
107 let mut size = bytes;
108 let mut unit_index = 0;
109
110 while size >= 1024.0 && unit_index < UNITS.len() - 1 {
111 size /= 1024.0;
112 unit_index += 1;
113 }
114
115 format!("{:.2} {}", size, UNITS[unit_index])
116}
117
118#[wasm_bindgen]
120pub fn generate_uuid() -> String {
121 let result = js_sys::eval(
123 "typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : null"
124 );
125
126 if let Ok(uuid) = result {
127 if let Some(uuid_str) = uuid.as_string() {
128 return uuid_str;
129 }
130 }
131
132 use getrandom::getrandom;
134 let mut bytes = [0u8; 16];
135 if getrandom(&mut bytes).is_ok() {
136 bytes[6] = (bytes[6] & 0x0f) | 0x40;
138 bytes[8] = (bytes[8] & 0x3f) | 0x80;
139
140 format!(
141 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
142 bytes[0], bytes[1], bytes[2], bytes[3],
143 bytes[4], bytes[5],
144 bytes[6], bytes[7],
145 bytes[8], bytes[9],
146 bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
147 )
148 } else {
149 format!("{}-{}", js_sys::Date::now(), js_sys::Math::random())
151 }
152}
153
154#[wasm_bindgen]
156pub fn is_local_storage_available() -> bool {
157 js_sys::eval("typeof localStorage !== 'undefined'")
158 .map(|v| v.is_truthy())
159 .unwrap_or(false)
160}
161
162#[wasm_bindgen]
164pub fn is_indexed_db_available() -> bool {
165 js_sys::eval("typeof indexedDB !== 'undefined'")
166 .map(|v| v.is_truthy())
167 .unwrap_or(false)
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_format_bytes() {
176 assert_eq!(format_bytes(100.0), "100.00 B");
177 assert_eq!(format_bytes(1024.0), "1.00 KB");
178 assert_eq!(format_bytes(1024.0 * 1024.0), "1.00 MB");
179 }
180}