use crate::error::Result;
use crate::server::channel::Channel;
use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
use serde_json::Value;
use std::any::Any;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct WebSocketRoute {
base: ChannelOwnerImpl,
url: String,
message_handlers: Arc<Mutex<Vec<WebSocketRouteMessageHandler>>>,
close_handlers: Arc<Mutex<Vec<WebSocketRouteCloseHandler>>>,
}
type WebSocketRouteHandlerFuture = Pin<Box<dyn Future<Output = Result<()>> + Send>>;
type WebSocketRouteMessageHandler =
Arc<dyn Fn(String) -> WebSocketRouteHandlerFuture + Send + Sync>;
type WebSocketRouteCloseHandler = Arc<dyn Fn() -> WebSocketRouteHandlerFuture + Send + Sync>;
impl WebSocketRoute {
pub fn new(
parent: Arc<dyn ChannelOwner>,
type_name: String,
guid: Arc<str>,
initializer: Value,
) -> Result<Self> {
let url = initializer["url"].as_str().unwrap_or("").to_string();
let base = ChannelOwnerImpl::new(
ParentOrConnection::Parent(parent),
type_name,
guid,
initializer,
);
Ok(Self {
base,
url,
message_handlers: Arc::new(Mutex::new(Vec::new())),
close_handlers: Arc::new(Mutex::new(Vec::new())),
})
}
pub fn url(&self) -> &str {
&self.url
}
pub async fn connect_to_server(&self) -> Result<()> {
self.base
.channel()
.send_no_result("connectToServer", serde_json::json!({}))
.await
}
pub async fn close(&self, options: Option<WebSocketRouteCloseOptions>) -> Result<()> {
let opts = options.unwrap_or_default();
let mut params = serde_json::Map::new();
if let Some(code) = opts.code {
params.insert("code".to_string(), serde_json::json!(code));
}
if let Some(reason) = opts.reason {
params.insert("reason".to_string(), serde_json::json!(reason));
}
self.base
.channel()
.send_no_result("close", Value::Object(params))
.await
}
pub async fn send(&self, message: &str) -> Result<()> {
self.base
.channel()
.send_no_result(
"sendToPage",
serde_json::json!({ "message": message, "isBase64": false }),
)
.await
}
pub async fn on_message<F>(&self, handler: F) -> Result<()>
where
F: Fn(String) -> WebSocketRouteHandlerFuture + Send + Sync + 'static,
{
let handler_arc = Arc::new(handler);
self.message_handlers.lock().unwrap().push(handler_arc);
Ok(())
}
pub async fn on_close<F>(&self, handler: F) -> Result<()>
where
F: Fn() -> WebSocketRouteHandlerFuture + Send + Sync + 'static,
{
let handler_arc = Arc::new(handler);
self.close_handlers.lock().unwrap().push(handler_arc);
Ok(())
}
pub(crate) fn handle_event(&self, event: &str, params: &Value) {
match event {
"messageFromPage" => {
let payload = params["message"].as_str().unwrap_or("").to_string();
let handlers = self.message_handlers.lock().unwrap().clone();
for handler in handlers {
let p = payload.clone();
tokio::spawn(async move {
let _ = handler(p).await;
});
}
}
"close" => {
let handlers = self.close_handlers.lock().unwrap().clone();
for handler in handlers {
tokio::spawn(async move {
let _ = handler().await;
});
}
}
_ => {}
}
}
}
#[derive(Debug, Default, Clone)]
pub struct WebSocketRouteCloseOptions {
pub code: Option<u16>,
pub reason: Option<String>,
}
impl ChannelOwner for WebSocketRoute {
fn guid(&self) -> &str {
self.base.guid()
}
fn type_name(&self) -> &str {
self.base.type_name()
}
fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
self.base.parent()
}
fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
self.base.connection()
}
fn initializer(&self) -> &Value {
self.base.initializer()
}
fn channel(&self) -> &Channel {
self.base.channel()
}
fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
self.base.dispose(reason)
}
fn adopt(&self, child: Arc<dyn ChannelOwner>) {
self.base.adopt(child)
}
fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
self.base.add_child(guid, child)
}
fn remove_child(&self, guid: &str) {
self.base.remove_child(guid)
}
fn on_event(&self, method: &str, params: Value) {
self.handle_event(method, ¶ms);
self.base.on_event(method, params)
}
fn was_collected(&self) -> bool {
self.base.was_collected()
}
fn as_any(&self) -> &dyn Any {
self
}
}