1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
//! [`DisconnectHandler`] trait and implementations, used to handle the disconnect event.
//! It has a flexible axum-like API, you can put any arguments as long as it implements the [`FromDisconnectParts`] trait.
//!
//! You can also implement the [`FromDisconnectParts`] trait for your own types.
//! See the [`extract`](super::extract) module doc for more details on available extractors.
//!
//! Handlers can be _optionally_ async.
//!
//! ## Example with sync closures
//! ```rust
//! # use socketioxide::SocketIo;
//! # use serde_json::Error;
//! # use socketioxide::extract::*;
//! # use socketioxide::socket::DisconnectReason;
//! let (svc, io) = SocketIo::new_svc();
//! io.ns("/", |s: SocketRef| {
//! s.on_disconnect(|s: SocketRef, reason: DisconnectReason| {
//! println!("Socket {} was disconnected because {} ", s.id, reason);
//! });
//! });
//! ```
//!
//! ## Example with async closures
//! ```rust
//! # use socketioxide::SocketIo;
//! # use serde_json::Error;
//! # use socketioxide::extract::*;
//! let (svc, io) = SocketIo::new_svc();
//! io.ns("/", |s: SocketRef| {
//! s.on_disconnect(move |s: SocketRef| async move {
//! println!("Socket {} was disconnected", s.id);
//! });
//! });
//! ```
//!
//! ## Example with async non anonymous functions
//! ```rust
//! # use socketioxide::SocketIo;
//! # use serde_json::Error;
//! # use socketioxide::extract::*;
//! # use socketioxide::socket::DisconnectReason;
//! async fn handler(s: SocketRef, reason: DisconnectReason) {
//! tokio::time::sleep(std::time::Duration::from_secs(1)).await;
//! println!("Socket disconnected on {} namespace with id and reason: {} {}", s.ns(), s.id, reason);
//! }
//!
//! let (svc, io) = SocketIo::new_svc();
//!
//! // You can reuse the same handler for multiple sockets
//! io.ns("/", |s: SocketRef| {
//! s.on_disconnect(handler);
//! });
//! io.ns("/admin", |s: SocketRef| {
//! s.on_disconnect(handler);
//! });
//! ```
use std::sync::Arc;
use futures::Future;
use crate::{
adapter::Adapter,
socket::{DisconnectReason, Socket},
};
use super::MakeErasedHandler;
/// A Type Erased [`DisconnectHandler`] so it can be stored in a HashMap
pub(crate) type BoxedDisconnectHandler<A> = Box<dyn ErasedDisconnectHandler<A>>;
pub(crate) trait ErasedDisconnectHandler<A: Adapter>: Send + Sync + 'static {
fn call(&self, s: Arc<Socket<A>>, reason: DisconnectReason);
}
impl<A: Adapter, T, H> MakeErasedHandler<H, A, T>
where
T: Send + Sync + 'static,
H: DisconnectHandler<A, T> + Send + Sync + 'static,
{
pub fn new_disconnect_boxed(inner: H) -> Box<dyn ErasedDisconnectHandler<A>> {
Box::new(MakeErasedHandler::new(inner))
}
}
impl<A: Adapter, T, H> ErasedDisconnectHandler<A> for MakeErasedHandler<H, A, T>
where
H: DisconnectHandler<A, T> + Send + Sync + 'static,
T: Send + Sync + 'static,
{
#[inline(always)]
fn call(&self, s: Arc<Socket<A>>, reason: DisconnectReason) {
self.handler.call(s, reason);
}
}
/// A trait used to extract the arguments from the disconnect event.
/// The `Result` associated type is used to return an error if the extraction fails,
/// in this case the [`DisconnectHandler`] is not called.
///
/// * See the [`disconnect`](super::disconnect) module doc for more details on disconnect handler.
/// * See the [`extract`](super::extract) module doc for more details on available extractors.
pub trait FromDisconnectParts<A: Adapter>: Sized {
/// The error type returned by the extractor
type Error: std::error::Error + 'static;
/// Extract the arguments from the disconnect event.
/// If it fails, the handler is not called
fn from_disconnect_parts(
s: &Arc<Socket<A>>,
reason: DisconnectReason,
) -> Result<Self, Self::Error>;
}
/// Define a handler for the disconnect event.
/// It is implemented for closures with up to 16 arguments. They must implement the [`FromDisconnectParts`] trait.
///
/// * See the [`disconnect`](super::disconnect) module doc for more details on disconnect handler.
/// * See the [`extract`](super::extract) module doc for more details on available extractors.
pub trait DisconnectHandler<A: Adapter, T>: Send + Sync + 'static {
/// Call the handler with the given arguments.
fn call(&self, s: Arc<Socket<A>>, reason: DisconnectReason);
#[doc(hidden)]
fn phantom(&self) -> std::marker::PhantomData<T> {
std::marker::PhantomData
}
}
mod private {
#[derive(Debug, Copy, Clone)]
pub enum Sync {}
#[derive(Debug, Copy, Clone)]
pub enum Async {}
}
macro_rules! impl_handler_async {
(
[$($ty:ident),*]
) => {
#[allow(non_snake_case, unused)]
impl<A, F, Fut, $($ty,)*> DisconnectHandler<A, (private::Async, $($ty,)*)> for F
where
F: FnOnce($($ty,)*) -> Fut + Send + Sync + Clone + 'static,
Fut: Future<Output = ()> + Send + 'static,
A: Adapter,
$( $ty: FromDisconnectParts<A> + Send, )*
{
fn call(&self, s: Arc<Socket<A>>, reason: DisconnectReason) {
$(
let $ty = match $ty::from_disconnect_parts(&s, reason) {
Ok(v) => v,
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("Error while extracting data: {}", _e);
return;
},
};
)*
let fut = (self.clone())($($ty,)*);
tokio::spawn(fut);
}
}
};
}
macro_rules! impl_handler {
(
[$($ty:ident),*]
) => {
#[allow(non_snake_case, unused)]
impl<A, F, $($ty,)*> DisconnectHandler<A, (private::Sync, $($ty,)*)> for F
where
F: FnOnce($($ty,)*) + Send + Sync + Clone + 'static,
A: Adapter,
$( $ty: FromDisconnectParts<A> + Send, )*
{
fn call(&self, s: Arc<Socket<A>>, reason: DisconnectReason) {
$(
let $ty = match $ty::from_disconnect_parts(&s, reason) {
Ok(v) => v,
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("Error while extracting data: {}", _e);
return;
},
};
)*
(self.clone())($($ty,)*);
}
}
};
}
#[rustfmt::skip]
macro_rules! all_the_tuples {
($name:ident) => {
$name!([]);
$name!([T1]);
$name!([T1, T2]);
$name!([T1, T2, T3]);
$name!([T1, T2, T3, T4]);
$name!([T1, T2, T3, T4, T5]);
$name!([T1, T2, T3, T4, T5, T6]);
$name!([T1, T2, T3, T4, T5, T6, T7]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15]);
$name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16]);
};
}
all_the_tuples!(impl_handler_async);
all_the_tuples!(impl_handler);