use std::future::Future;
use std::sync::Arc;
use socketioxide_core::Value;
use crate::adapter::Adapter;
use crate::socket::Socket;
use super::MakeErasedHandler;
pub(crate) type BoxedMessageHandler<A> = Box<dyn ErasedMessageHandler<A>>;
pub(crate) trait ErasedMessageHandler<A: Adapter>: Send + Sync + 'static {
fn call(&self, s: Arc<Socket<A>>, v: Value, ack_id: Option<i64>);
}
#[diagnostic::on_unimplemented(
note = "This function is not a MessageHandler. Check that:
* It is a clonable async `FnOnce` that returns nothing.
* All its arguments are valid message extractors.
* If you use a custom adapter, it must be generic over the adapter type.
See `https://docs.rs/socketioxide/latest/socketioxide/extract/index.html` for details.\n",
label = "Invalid MessageHandler"
)]
pub trait MessageHandler<A: Adapter, T>: Send + Sync + 'static {
fn call(&self, s: Arc<Socket<A>>, v: Value, ack_id: Option<i64>);
#[doc(hidden)]
fn phantom(&self) -> std::marker::PhantomData<T> {
std::marker::PhantomData
}
}
impl<A, T, H> MakeErasedHandler<H, A, T>
where
T: Send + Sync + 'static,
H: MessageHandler<A, T>,
A: Adapter,
{
pub fn new_message_boxed(inner: H) -> Box<dyn ErasedMessageHandler<A>> {
Box::new(MakeErasedHandler::new(inner))
}
}
impl<A, T, H> ErasedMessageHandler<A> for MakeErasedHandler<H, A, T>
where
T: Send + Sync + 'static,
H: MessageHandler<A, T>,
A: Adapter,
{
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self, s), fields(id = ?s.id)))]
#[inline(always)]
fn call(&self, s: Arc<Socket<A>>, v: Value, ack_id: Option<i64>) {
self.handler.call(s, v, ack_id);
}
}
mod private {
#[derive(Debug, Clone, Copy)]
pub enum ViaParts {}
#[derive(Debug, Clone, Copy)]
pub enum ViaRequest {}
}
#[diagnostic::on_unimplemented(
note = "This function argument is not a valid socketio extractor.
See `https://docs.rs/socketioxide/latest/socketioxide/extract/index.html` for details\n",
label = "Invalid extractor"
)]
pub trait FromMessageParts<A: Adapter>: Sized {
type Error: std::error::Error + 'static;
fn from_message_parts(
s: &Arc<Socket<A>>,
v: &mut Value,
ack_id: &Option<i64>,
) -> Result<Self, Self::Error>;
}
#[diagnostic::on_unimplemented(
note = "This function argument is not a valid socketio extractor.
See `https://docs.rs/socketioxide/latest/socketioxide/extract/index.html` for details\n",
label = "Invalid extractor"
)]
pub trait FromMessage<A: Adapter, M = private::ViaRequest>: Sized {
type Error: std::error::Error + 'static;
fn from_message(s: Arc<Socket<A>>, v: Value, ack_id: Option<i64>) -> Result<Self, Self::Error>;
}
impl<A, T> FromMessage<A, private::ViaParts> for T
where
T: FromMessageParts<A>,
A: Adapter,
{
type Error = T::Error;
fn from_message(
s: Arc<Socket<A>>,
mut v: Value,
ack_id: Option<i64>,
) -> Result<Self, Self::Error> {
Self::from_message_parts(&s, &mut v, &ack_id)
}
}
impl<A, F, Fut> MessageHandler<A, ()> for F
where
F: FnOnce() -> Fut + Send + Sync + Clone + 'static,
Fut: Future<Output = ()> + Send + 'static,
A: Adapter,
{
fn call(&self, _: Arc<Socket<A>>, _: Value, _: Option<i64>) {
let fut = (self.clone())();
tokio::spawn(fut);
}
}
macro_rules! impl_async_handler {
(
[$($ty:ident),*], $last:ident
) => {
#[allow(non_snake_case, unused)]
#[diagnostic::do_not_recommend]
impl<A, F, M, $($ty,)* $last, Fut> MessageHandler<A, (M, $($ty,)* $last,)> for F
where
F: FnOnce($($ty,)* $last,) -> Fut + Send + Sync + Clone + 'static,
Fut: Future<Output = ()> + Send + 'static,
A: Adapter,
$( $ty: FromMessageParts<A> + Send, )*
$last: FromMessage<A, M> + Send,
{
fn call(&self, s: Arc<Socket<A>>, mut v: Value, ack_id: Option<i64>) {
$(
let $ty = match $ty::from_message_parts(&s, &mut v, &ack_id) {
Ok(v) => v,
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("Error while extracting data: {}", _e);
return;
},
};
)*
let last = match $last::from_message(s, v, ack_id) {
Ok(v) => v,
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("Error while extracting data: {}", _e);
return;
},
};
let fut = (self.clone())($($ty,)* last);
tokio::spawn(fut);
}
}
};
}
#[rustfmt::skip]
macro_rules! all_the_tuples {
($name:ident) => {
$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_async_handler);