1use alloc::rc::Rc;
68use alloc::rc::Weak;
69
70use wasm_bindgen02::JsCast;
71use wasm_bindgen02::closure::Closure;
72use web_sys03::js_sys::{ArrayBuffer, Math, Uint8Array};
73use web_sys03::{BinaryType, CloseEvent, ErrorEvent, MessageEvent, WebSocket, Window, window};
74
75use crate::web::{
76 Connect, EmptyCallback, Error, Location, ServiceBuilder, Shared, SocketImpl, WebImpl,
77 WindowImpl,
78};
79
80pub mod prelude {
81 pub mod ws {
84 pub use crate::api::ChannelId;
88 pub use crate::web::{
89 Connect, EmptyCallback, Error, Listener, Packet, RawPacket, Request, State,
90 StateListener,
91 };
92
93 use crate::web03::Web03Impl;
94
95 pub fn connect(connect: Connect) -> ServiceBuilder<EmptyCallback> {
99 crate::web03::connect(connect)
100 }
101
102 pub type Service = crate::web::Service<Web03Impl>;
106
107 pub type Handle = crate::web::Handle<Web03Impl>;
111
112 pub type Channel = crate::web::Channel<Web03Impl>;
116
117 pub type RequestBuilder<'a, B, C> = crate::web::RequestBuilder<'a, Web03Impl, B, C>;
121
122 pub type ServiceBuilder<C> = crate::web::ServiceBuilder<Web03Impl, C>;
126 }
127}
128
129#[doc(hidden)]
131pub struct Handles {
132 close: Closure<dyn Fn(CloseEvent)>,
133 message: Closure<dyn Fn(MessageEvent)>,
134 error: Closure<dyn Fn(ErrorEvent)>,
135}
136
137#[derive(Clone, Copy)]
141pub enum Web03Impl {}
142
143impl crate::web::sealed_socket::Sealed for WebSocket {}
144
145impl SocketImpl for WebSocket {
146 type Handles = Handles;
147
148 #[inline]
149 fn new(url: &str, handles: &Self::Handles) -> Result<Self, Error> {
150 let this = WebSocket::new(url)?;
151 this.set_binary_type(BinaryType::Arraybuffer);
152 this.set_onclose(Some(handles.close.as_ref().unchecked_ref()));
153 this.set_onmessage(Some(handles.message.as_ref().unchecked_ref()));
154 this.set_onerror(Some(handles.error.as_ref().unchecked_ref()));
155 Ok(this)
156 }
157
158 #[inline]
159 fn send(&self, data: &[u8]) -> Result<(), Error> {
160 self.send_with_u8_array(data)?;
161 Ok(())
162 }
163
164 #[inline]
165 fn close(self) -> Result<(), Error> {
166 WebSocket::set_onclose(&self, None);
169 WebSocket::set_onmessage(&self, None);
170 WebSocket::set_onerror(&self, None);
171 WebSocket::close(&self)?;
172 Ok(())
173 }
174}
175
176impl crate::web::sealed_window::Sealed for Window {}
177
178impl WindowImpl for Window {
179 type Timeout = Timeout;
180 type OnBeforeUnload = Event;
181
182 #[inline]
183 fn new() -> Result<Self, Error> {
184 let Some(window) = window() else {
185 return Err(Error::message("No window in web-sys 0.3.x context"));
186 };
187
188 Ok(window)
189 }
190
191 #[inline]
192 fn location(&self) -> Result<Location, Error> {
193 let location = Window::location(self);
194
195 Ok(Location {
196 protocol: location.protocol()?,
197 host: location.hostname()?,
198 port: location.port()?,
199 })
200 }
201
202 #[inline]
203 fn onbeforeunload(&self, callback: impl Fn() + 'static) -> Result<Self::OnBeforeUnload, Error> {
204 let closure = Closure::new(callback);
205 window()
206 .unwrap()
207 .set_onbeforeunload(Some(closure.as_ref().unchecked_ref()));
208 Ok(Event { closure })
209 }
210
211 #[inline]
212 fn set_timeout(
213 &self,
214 millis: u32,
215 callback: impl Fn() + 'static,
216 ) -> Result<Self::Timeout, Error> {
217 let closure = Closure::new(callback);
218
219 let id = self.set_timeout_with_callback_and_timeout_and_arguments_0(
220 closure.as_ref().unchecked_ref(),
221 millis as i32,
222 )?;
223
224 Ok(Timeout {
225 window: self.clone(),
226 id: Some(id),
227 closure: Some(closure),
228 })
229 }
230}
231
232pub struct Timeout {
233 window: Window,
234 id: Option<i32>,
235 #[allow(dead_code)]
236 closure: Option<Closure<dyn FnMut()>>,
237}
238
239impl Drop for Timeout {
240 fn drop(&mut self) {
243 if let Some(id) = self.id.take() {
244 self.window.clear_timeout_with_handle(id);
245 }
246 }
247}
248
249pub struct Event {
250 #[allow(dead_code)]
251 closure: Closure<dyn FnMut()>,
252}
253
254impl crate::web::sealed_web::Sealed for Web03Impl {}
255
256impl WebImpl for Web03Impl {
257 type Window = Window;
258 type Handles = Handles;
259 type Socket = WebSocket;
260
261 #[inline]
262 fn random(range: u32) -> u32 {
263 ((Math::random() * range as f64).round() as u32).min(range)
264 }
265
266 #[inline]
267 #[allow(private_interfaces)]
268 fn handles(shared: &Weak<Shared<Self>>) -> Self::Handles {
269 let close = {
270 let shared = shared.clone();
271
272 Closure::new(move |e: CloseEvent| {
273 if let Some(shared) = shared.upgrade() {
274 shared.web03_close(e);
275 }
276 })
277 };
278
279 let message = {
280 let shared = shared.clone();
281
282 Closure::new(move |e: MessageEvent| {
283 if let Some(shared) = shared.upgrade() {
284 shared.web03_message(e);
285 }
286 })
287 };
288
289 let error = {
290 let shared = shared.clone();
291
292 Closure::new(move |e: ErrorEvent| {
293 if let Some(shared) = shared.upgrade() {
294 shared.web03_error(e);
295 }
296 })
297 };
298
299 Self::Handles {
300 close,
301 message,
302 error,
303 }
304 }
305}
306
307#[inline]
310pub fn connect(connect: Connect) -> ServiceBuilder<Web03Impl, EmptyCallback> {
311 crate::web::connect(connect)
312}
313
314impl Shared<Web03Impl> {
315 fn web03_close(self: &Rc<Self>, e: CloseEvent) {
316 tracing::debug!(code = e.code(), reason = e.reason(), "Close event");
317
318 if let Err(e) = self.close_and_reconnect() {
319 self.on_error.call(e);
320 }
321 }
322
323 fn web03_message(self: &Rc<Shared<Web03Impl>>, e: MessageEvent) {
324 tracing::debug!("Message event");
325
326 let Ok(array_buffer) = e.data().dyn_into::<ArrayBuffer>() else {
327 self.on_error
328 .call(Error::message("Expected message as ArrayBuffer"));
329 return;
330 };
331
332 let array = Uint8Array::new(&array_buffer);
333 let needed = array.length() as usize;
334
335 let mut buf = self.next_buffer(needed);
336
337 unsafe {
339 array.raw_copy_to_ptr(buf.data.as_mut_ptr());
340 buf.data.set_len(needed);
341 }
342
343 if let Err(e) = self.message(buf) {
344 self.on_error.call(e);
345 }
346 }
347
348 fn web03_error(self: &Rc<Self>, e: ErrorEvent) {
349 tracing::debug!(message = e.message(), "Error event");
350
351 if let Err(e) = self.close_and_reconnect() {
352 self.on_error.call(e);
353 }
354 }
355}