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<RtcDataChannelState> 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<RefCell<Option<JsValue>>>,
}
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<OnStateChange>) {
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<OnMessage>) {
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<OnBufferedAmountChange>) {
todo!("onbufferedamountlow instead?")
}
}