use std::sync::Arc;
use crate::*;
use gloo_utils::format::JsValueSerdeExt;
use serde_wasm_bindgen::{Deserializer, Serializer};
use wasm_bindgen::UnwrapThrowExt;
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
#[wasm_bindgen]
pub struct EZRpc {
session: Arc<Session>,
}
pub struct AnyErr(anyhow::Error);
impl<T: Into<anyhow::Error>> From<T> for AnyErr {
fn from(value: T) -> Self {
Self(value.into())
}
}
impl From<AnyErr> for JsValue {
fn from(value: AnyErr) -> Self {
JsValue::from_str(&format!("{:#?}", value.0))
}
}
impl AnyErr {
fn msg<T: core::fmt::Debug>(err: T) -> Self {
Self(anyhow::anyhow!("{err:?}"))
}
}
pub type AnyRes<T> = Result<T, AnyErr>;
pub struct JsService(js_sys::Object);
unsafe impl Send for JsService {}
unsafe impl Sync for JsService {}
impl Service for JsService {
fn handle<'a>(&'a self, ss: &'a Arc<Session>, pack: Packet) -> ServiceResult<'a> {
let key = JsValue::from_serde(pack.method().unwrap()).unwrap_throw();
let func = js_sys::Reflect::get(&self.0, &key).unwrap_throw();
let func: js_sys::Function = func.dyn_into().unwrap_throw();
let args = pack
.decode_to(
&Serializer::new()
.serialize_missing_as_null(true)
.serialize_maps_as_objects(true),
)
.unwrap_throw();
let res = match func.length() {
0 => func.call0(&self.0),
1 => func.call1(&self.0, &args),
_ => func.apply(&self.0, args.dyn_ref().unwrap_throw()),
};
match res {
Ok(ret) => {
let ss = ss.clone();
return Box::pin(async move {
let val = match ret.dyn_into::<js_sys::Promise>() {
Ok(prom) => match wasm_bindgen_futures::JsFuture::from(prom).await {
Ok(val) => val,
Err(err) => {
gloo_console::error!("ezrpc service error: {:?}", err);
JsValue::NULL
}
},
Err(val) => val,
};
if let Some(id) = pack.request_id() {
ss.response_de(id, Deserializer::from(val)).await.ok();
}
Ok(())
});
}
Err(err) => {
gloo_console::error!("ezrpc service error: {:?}", err);
}
}
Box::pin(async { Ok(()) })
}
}
#[wasm_bindgen]
impl EZRpc {
#[wasm_bindgen(constructor)]
pub async fn new(addr: &str, service: js_sys::Object) -> AnyRes<EZRpc> {
let a = ws::WsAdapter::connect(addr).await?;
let session = Arc::new(Session::new(Arc::new(a), Arc::new(JsService(service))));
Ok(Self { session })
}
pub async fn notify(&self, method: JsValue, args: JsValue) -> AnyRes<()> {
self.session
.notify_de(Deserializer::from(method), Deserializer::from(args))
.await?;
Ok(())
}
pub async fn request(&self, method: JsValue, args: JsValue) -> AnyRes<JsValue> {
Ok(self
.session
.request_de(
Deserializer::from(method),
Deserializer::from(args),
&Serializer::new()
.serialize_missing_as_null(true)
.serialize_maps_as_objects(true),
)
.await?)
}
pub async fn request_recver(&self, method: JsValue, args: JsValue) -> AnyRes<RespRecver> {
Ok(self
.session
.request_recver_de(Deserializer::from(method), Deserializer::from(args))
.await
.map(RespRecver)?)
}
pub async fn loop_dispatch(&self) -> AnyRes<()> {
Ok(self.session.loop_dispatch().await?)
}
}
#[wasm_bindgen]
pub struct RespRecver(StreamReceiver);
#[wasm_bindgen]
impl RespRecver {
pub async fn next(&mut self) -> AnyRes<JsValue> {
if let Some(p) = self.0.recv().await? {
Ok(p.decode_to(
&Serializer::new()
.serialize_missing_as_null(true)
.serialize_maps_as_objects(true),
)
.map_err(AnyErr::msg)?)
} else {
Ok(JsValue::NULL)
}
}
}