1use wasm_bindgen::prelude::*;
6use web_sys::console;
7
8pub fn set_panic_hook() {
10 #[cfg(feature = "console_error_panic_hook")]
17 console_error_panic_hook::set_once();
18}
19
20#[wasm_bindgen]
22pub fn log(message: &str) {
23 console::log_1(&message.into());
24}
25
26#[wasm_bindgen]
28pub fn log_error(message: &str) {
29 console::error_1(&message.into());
30}
31
32#[wasm_bindgen]
34pub fn log_warn(message: &str) {
35 console::warn_1(&message.into());
36}
37
38#[wasm_bindgen]
40pub struct Performance;
41
42#[wasm_bindgen]
43impl Performance {
44 #[wasm_bindgen(js_name = "now")]
46 pub fn now() -> f64 {
47 js_sys::Date::now()
48 }
49
50 #[wasm_bindgen(js_name = "measure")]
52 pub fn measure(name: &str, start: f64) -> f64 {
53 let duration = Self::now() - start;
54 log(&format!("{}: {}ms", name, duration));
55 duration
56 }
57}
58
59#[wasm_bindgen]
61pub struct Memory;
62
63#[wasm_bindgen]
64impl Memory {
65 #[wasm_bindgen(js_name = "getUsage")]
67 pub fn get_usage() -> JsValue {
68 if let Ok(memory) = js_sys::Reflect::get(&wasm_bindgen::memory(), &"buffer".into()) {
69 if let Some(buffer) = memory.dyn_ref::<js_sys::ArrayBuffer>() {
70 let obj = js_sys::Object::new();
71 js_sys::Reflect::set(
72 &obj,
73 &"bytes".into(),
74 &buffer.byte_length().into(),
75 ).unwrap();
76 return obj.into();
77 }
78 }
79 JsValue::NULL
80 }
81}
82
83#[wasm_bindgen]
85pub struct Encoding;
86
87#[wasm_bindgen]
88impl Encoding {
89 #[wasm_bindgen(js_name = "bytesToHex")]
91 pub fn bytes_to_hex(bytes: &[u8]) -> String {
92 hex::encode(bytes)
93 }
94
95 #[wasm_bindgen(js_name = "hexToBytes")]
97 pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>, JsError> {
98 hex::decode(hex)
99 .map_err(|e| JsError::new(&format!("Invalid hex string: {}", e)))
100 }
101
102 #[wasm_bindgen(js_name = "stringToBytes")]
104 pub fn string_to_bytes(s: &str) -> Vec<u8> {
105 s.as_bytes().to_vec()
106 }
107
108 #[wasm_bindgen(js_name = "bytesToString")]
110 pub fn bytes_to_string(bytes: &[u8]) -> Result<String, JsError> {
111 std::string::String::from_utf8(bytes.to_vec())
112 .map_err(|e| JsError::new(&format!("Invalid UTF-8: {}", e)))
113 }
114}
115
116#[wasm_bindgen]
118pub struct Validation;
119
120#[wasm_bindgen]
121impl Validation {
122 #[wasm_bindgen(js_name = "isDarkDomain")]
124 pub fn is_dark_domain(domain: &str) -> bool {
125 domain.ends_with(".dark") && domain.len() > 5
126 }
127
128 #[wasm_bindgen(js_name = "isPeerAddress")]
130 pub fn is_peer_address(address: &str) -> bool {
131 address.starts_with("/ip4/") || address.starts_with("/ip6/")
133 }
134
135 #[wasm_bindgen(js_name = "isValidHex")]
137 pub fn is_valid_hex(hex: &str) -> bool {
138 hex.chars().all(|c| c.is_ascii_hexdigit())
139 }
140}
141
142#[wasm_bindgen]
144pub struct Random;
145
146#[wasm_bindgen]
147impl Random {
148 #[wasm_bindgen(js_name = "getBytes")]
150 pub fn get_bytes(length: usize) -> Result<Vec<u8>, JsError> {
151 let mut bytes = vec![0u8; length];
152 getrandom::getrandom(&mut bytes)
153 .map_err(|e| JsError::new(&format!("Failed to generate random bytes: {}", e)))?;
154 Ok(bytes)
155 }
156
157 #[wasm_bindgen(js_name = "getId")]
159 pub fn get_id() -> String {
160 let bytes = Self::get_bytes(16).unwrap_or_else(|_| vec![0u8; 16]);
161 hex::encode(bytes)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use wasm_bindgen_test::*;
169
170 #[wasm_bindgen_test]
171 fn test_encoding_roundtrip() {
172 let original = "Hello, QuDAG!";
173 let bytes = Encoding::string_to_bytes(original);
174 let hex = Encoding::bytes_to_hex(&bytes);
175 let decoded_bytes = Encoding::hex_to_bytes(&hex).unwrap();
176 let decoded = Encoding::bytes_to_string(&decoded_bytes).unwrap();
177 assert_eq!(original, decoded);
178 }
179
180 #[wasm_bindgen_test]
181 fn test_validation() {
182 assert!(Validation::is_dark_domain("test.dark"));
183 assert!(!Validation::is_dark_domain("test.com"));
184 assert!(Validation::is_peer_address("/ip4/127.0.0.1/tcp/8000"));
185 assert!(Validation::is_valid_hex("deadbeef"));
186 assert!(!Validation::is_valid_hex("not-hex"));
187 }
188}