use std::cell::{Cell, RefCell};
use std::rc::Rc;
use js_sys::Function;
use wasm_bindgen::prelude::*;
use super::adapters::TcpGatewayServerAdapter;
use super::binding_types::WasmTcpGatewayConfig;
use super::bridge::JsServerHandler;
#[wasm_bindgen]
pub struct WasmTcpServer {
config: WasmTcpGatewayConfig,
adapter: Rc<RefCell<TcpGatewayServerAdapter>>,
running: Rc<Cell<bool>>,
bridge: JsServerHandler,
dispatched_requests: Cell<u32>,
sent_frames: Cell<u32>,
received_frames: Cell<u32>,
last_error: RefCell<Option<String>>,
}
impl WasmTcpServer {
fn capture_error(&self, e: JsValue) -> JsValue {
let msg = e.as_string().unwrap_or_else(|| format!("{e:?}"));
*self.last_error.borrow_mut() = Some(msg);
e
}
}
#[wasm_bindgen]
impl WasmTcpServer {
#[wasm_bindgen(constructor)]
pub fn new(config: WasmTcpGatewayConfig, on_request: Function) -> Result<Self, JsValue> {
let adapter = TcpGatewayServerAdapter::new(&config)?;
Ok(Self {
config,
adapter: Rc::new(RefCell::new(adapter)),
running: Rc::new(Cell::new(false)),
bridge: JsServerHandler::new(on_request),
dispatched_requests: Cell::new(0),
sent_frames: Cell::new(0),
received_frames: Cell::new(0),
last_error: RefCell::new(None),
})
}
pub fn start(&self) -> Result<(), JsValue> {
if self.running.get() {
return Ok(());
}
self.adapter
.borrow_mut()
.connect()
.map_err(|e| self.capture_error(e))?;
self.running.set(true);
Ok(())
}
pub fn stop(&self) -> Result<(), JsValue> {
self.adapter
.borrow_mut()
.disconnect()
.map_err(|e| self.capture_error(e))?;
self.running.set(false);
Ok(())
}
pub fn is_running(&self) -> bool {
self.running.get()
}
pub fn transport_connected(&self) -> bool {
self.adapter.borrow().is_connected()
}
pub fn transport_connecting(&self) -> bool {
self.adapter.borrow().is_connecting()
}
pub fn ws_url(&self) -> String {
self.config.ws_url()
}
pub fn send_frame(&self, frame: &[u8]) -> Result<(), JsValue> {
self.adapter
.borrow_mut()
.send_frame(frame)
.map_err(|e| self.capture_error(e))?;
self.sent_frames.set(self.sent_frames.get() + 1);
Ok(())
}
pub fn recv_frame(&self) -> Result<Vec<u8>, JsValue> {
let frame = self
.adapter
.borrow_mut()
.recv_frame()
.map_err(|e| self.capture_error(e))?;
match frame {
Some(frame) => {
self.received_frames.set(self.received_frames.get() + 1);
Ok(frame.as_slice().to_vec())
}
None => Ok(Vec::new()),
}
}
pub async fn dispatch_request(&self, request: JsValue) -> Result<JsValue, JsValue> {
if !self.is_running() {
return Err(self.capture_error(JsValue::from_str("server is not running")));
}
let out = self
.bridge
.dispatch(request)
.await
.map_err(|e| self.capture_error(e))?;
self.dispatched_requests
.set(self.dispatched_requests.get() + 1);
Ok(out)
}
pub fn status_snapshot(&self) -> super::binding_types::WasmServerStatusSnapshot {
super::binding_types::WasmServerStatusSnapshot::new(
super::binding_types::WasmServerTransportKind::TcpGateway,
self.is_running(),
self.transport_connected(),
self.dispatched_requests.get(),
self.sent_frames.get(),
self.received_frames.get(),
self.last_error.borrow().is_some(),
)
}
pub fn last_error_message(&self) -> Option<String> {
self.last_error.borrow().clone()
}
pub fn clear_last_error(&self) {
self.last_error.borrow_mut().take();
}
}