use alloc::rc::Rc;
use alloc::rc::Weak;
use wasm_bindgen02::JsCast;
use wasm_bindgen02::closure::Closure;
use web_sys03::Performance;
use web_sys03::Window;
use web_sys03::js_sys::Function;
use web_sys03::js_sys::{ArrayBuffer, Math, Uint8Array};
use web_sys03::{BinaryType, CloseEvent, ErrorEvent, MessageEvent, WebSocket, window};
use crate::web::{
Connect, EmptyCallback, Error, Location, PerformanceImpl, ServiceBuilder, Shared, SocketImpl,
WebImpl, WindowImpl,
};
pub mod prelude {
pub mod ws {
pub use crate::api::ChannelId;
pub use crate::web::{
Connect, EmptyCallback, Error, Listener, Packet, RawPacket, Request, State,
StateListener,
};
use crate::web03::Web03Impl;
pub fn connect(connect: Connect) -> ServiceBuilder<EmptyCallback> {
crate::web03::connect(connect)
}
pub type Service = crate::web::Service<Web03Impl>;
pub type Handle = crate::web::Handle<Web03Impl>;
pub type Channel = crate::web::Channel<Web03Impl>;
pub type RequestBuilder<'a, B, C> = crate::web::RequestBuilder<'a, Web03Impl, B, C>;
pub type ServiceBuilder<C> = crate::web::ServiceBuilder<Web03Impl, C>;
}
}
#[doc(hidden)]
pub struct Handles {
close: Closure<dyn Fn(CloseEvent)>,
message: Closure<dyn Fn(MessageEvent)>,
error: Closure<dyn Fn(ErrorEvent)>,
}
#[derive(Clone, Copy)]
pub enum Web03Impl {}
impl crate::web::sealed_socket::Sealed for WebSocket {}
impl SocketImpl for WebSocket {
type Handles = Handles;
#[inline]
fn new(url: &str, handles: &Self::Handles) -> Result<Self, Error> {
let this = WebSocket::new(url)?;
this.set_binary_type(BinaryType::Arraybuffer);
this.set_onclose(Some(handles.close.as_ref().unchecked_ref()));
this.set_onmessage(Some(handles.message.as_ref().unchecked_ref()));
this.set_onerror(Some(handles.error.as_ref().unchecked_ref()));
Ok(this)
}
#[inline]
fn send(&self, data: &[u8]) -> Result<(), Error> {
self.send_with_u8_array(data)?;
Ok(())
}
#[inline]
fn close(self) -> Result<(), Error> {
WebSocket::close(&self)?;
Ok(())
}
}
impl crate::web::sealed_performance::Sealed for Performance {}
impl PerformanceImpl for Performance {
#[inline]
fn now(&self) -> f64 {
Performance::now(self)
}
}
impl crate::web::sealed_window::Sealed for Window {}
impl WindowImpl for Window {
type Performance = Performance;
type Timeout = Timeout;
#[inline]
fn new() -> Result<Self, Error> {
let Some(window) = window() else {
return Err(Error::msg("No window in web-sys 0.3.x context"));
};
Ok(window)
}
#[inline]
fn performance(&self) -> Result<Self::Performance, Error> {
let Some(performance) = Window::performance(self) else {
return Err(Error::msg("No window.performance in web-sys 0.3.x context"));
};
Ok(performance)
}
#[inline]
fn location(&self) -> Result<Location, Error> {
let location = Window::location(self);
Ok(Location {
protocol: location.protocol()?,
host: location.hostname()?,
port: location.port()?,
})
}
#[inline]
fn set_timeout(
&self,
millis: u32,
callback: impl FnOnce() + 'static,
) -> Result<Self::Timeout, Error> {
let closure = Closure::once(callback);
let id = self.set_timeout_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref::<Function>(),
millis as i32,
)?;
Ok(Timeout {
window: self.clone(),
id: Some(id),
closure: Some(closure),
})
}
}
pub struct Timeout {
window: Window,
id: Option<i32>,
#[allow(dead_code)]
closure: Option<Closure<dyn FnMut()>>,
}
impl Drop for Timeout {
fn drop(&mut self) {
if let Some(id) = self.id.take() {
self.window.clear_timeout_with_handle(id);
}
}
}
impl crate::web::sealed_web::Sealed for Web03Impl {}
impl WebImpl for Web03Impl {
type Window = Window;
type Handles = Handles;
type Socket = WebSocket;
#[inline]
fn random(range: u32) -> u32 {
((Math::random() * range as f64).round() as u32).min(range)
}
#[inline]
#[allow(private_interfaces)]
fn handles(shared: &Weak<Shared<Self>>) -> Self::Handles {
let close = {
let shared = shared.clone();
Closure::new(move |e: CloseEvent| {
if let Some(shared) = shared.upgrade() {
shared.web03_close(e);
}
})
};
let message = {
let shared = shared.clone();
Closure::new(move |e: MessageEvent| {
if let Some(shared) = shared.upgrade() {
shared.web03_message(e);
}
})
};
let error = {
let shared = shared.clone();
Closure::new(move |e: ErrorEvent| {
if let Some(shared) = shared.upgrade() {
shared.web03_error(e);
}
})
};
Self::Handles {
close,
message,
error,
}
}
}
#[inline]
pub fn connect(connect: Connect) -> ServiceBuilder<Web03Impl, EmptyCallback> {
crate::web::connect(connect)
}
impl Shared<Web03Impl> {
fn web03_close(self: &Rc<Self>, e: CloseEvent) {
tracing::debug!(code = e.code(), reason = e.reason(), "Close event");
if let Err(e) = self.close() {
self.on_error.call(e);
}
}
fn web03_message(self: &Rc<Shared<Web03Impl>>, e: MessageEvent) {
tracing::trace!("Message event");
let Ok(array_buffer) = e.data().dyn_into::<ArrayBuffer>() else {
self.on_error
.call(Error::msg("Expected message as ArrayBuffer"));
return;
};
let array = Uint8Array::new(&array_buffer);
let needed = array.length() as usize;
let mut buf = self.next_buffer(needed);
unsafe {
array.raw_copy_to_ptr(buf.data.as_mut_ptr());
buf.data.set_len(needed);
}
if let Err(e) = self.message(buf) {
self.on_error.call(e);
}
}
fn web03_error(self: &Rc<Self>, e: ErrorEvent) {
tracing::trace!(message = e.message(), "Error event");
if let Err(e) = self.close() {
self.on_error.call(e);
}
}
}