use core::str; use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::prelude::*; use web_sys::{MessageEvent, RtcDataChannelEvent, RtcDataChannelState}; use crate::data_channel::{ DataChannelError, DataChannelTrait, DataState, OnBufferedAmountChange, OnMessage, OnStateChange, }; impl From for DataState { fn from(value: RtcDataChannelState) -> Self { match value { RtcDataChannelState::Connecting => Self::Connecting, RtcDataChannelState::Open => Self::Open, RtcDataChannelState::Closing => Self::Closing, RtcDataChannelState::Closed => Self::Closed, _ => panic!("unknown data channel state"), } } } #[derive(Clone)] pub struct DataChannel { sys_handle: web_sys::RtcDataChannel, on_closing: Rc>>, } impl DataChannelTrait for DataChannel { fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> { if binary { self.sys_handle .send_with_u8_array(data) .map_err(|_| DataChannelError::Send) } else { let utf8 = str::from_utf8(data)?; self.sys_handle .send_with_str(utf8) .map_err(|_| DataChannelError::Send) } } fn label(&self) -> String { self.sys_handle.label() } fn state(&self) -> DataState { self.sys_handle.ready_state().into() } fn close(&self) { self.sys_handle.close(); } fn on_state_change(&self, callback: Option) { if let Some(mut callback) = callback { let dc = self.clone(); let js_callback = Closure::new(move |_: RtcDataChannelEvent| { callback(dc.state()); }); let js_callback = js_callback.into_js_value(); self.sys_handle .set_onopen(Some(js_callback.unchecked_ref())); self.sys_handle .set_onclose(Some(js_callback.unchecked_ref())); self.sys_handle .add_event_listener_with_callback("closing", js_callback.unchecked_ref()) .unwrap(); self.on_closing.replace(Some(js_callback)); } else { self.sys_handle.set_onopen(None); self.sys_handle.set_onclose(None); if let Some(on_closing) = self.on_closing.take() { self.sys_handle .remove_event_listener_with_callback("closing", on_closing.unchecked_ref()) .unwrap(); } self.on_closing.replace(None); } } fn on_message(&self, callback: Option) { let js_callback = callback.map(|mut callback| { Closure::new(move |event: MessageEvent| { if let Some(str) = event.as_string() { callback(str.as_bytes(), false); } }) .into_js_value() }); self.sys_handle.set_onmessage( js_callback .as_ref() .map(|callback| callback.unchecked_ref()), ); } fn on_buffered_amount_change(&self, _callback: Option) { todo!("onbufferedamountlow instead?") } }