use crate::{Conn, ConnExt, Result};
use std::{
any::Any,
borrow::Cow,
fmt::{self, Debug, Formatter},
future::Future,
pin::Pin,
sync::Arc,
};
pub trait ClientHandler: Send + Sync + 'static {
fn run(&self, conn: &mut Conn) -> impl Future<Output = Result<()>> + Send {
let _ = conn;
async { Ok(()) }
}
fn after_response(&self, conn: &mut Conn) -> impl Future<Output = Result<()>> + Send {
let _ = conn;
async { Ok(()) }
}
fn name(&self) -> Cow<'static, str> {
std::any::type_name::<Self>().into()
}
}
pub(crate) trait ObjectSafeClientHandler: Any + Send + Sync + 'static {
fn run<'a>(
&'a self,
conn: &'a mut Conn,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
fn after_response<'a>(
&'a self,
conn: &'a mut Conn,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>;
fn name(&self) -> Cow<'static, str>;
fn as_any(&self) -> &dyn Any;
}
impl<H: ClientHandler> ObjectSafeClientHandler for H {
fn run<'a>(
&'a self,
conn: &'a mut Conn,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(ClientHandler::run(self, conn))
}
fn after_response<'a>(
&'a self,
conn: &'a mut Conn,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(ClientHandler::after_response(self, conn))
}
fn name(&self) -> Cow<'static, str> {
ClientHandler::name(self)
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Clone)]
pub(crate) struct ArcedClientHandler(Arc<dyn ObjectSafeClientHandler>);
impl Debug for ArcedClientHandler {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("ArcedClientHandler")
.field(&self.0.name())
.finish()
}
}
impl ArcedClientHandler {
pub(crate) fn new(handler: impl ClientHandler) -> Self {
Self(Arc::new(handler))
}
pub(crate) fn downcast_ref<T: Any + 'static>(&self) -> Option<&T> {
self.0.as_any().downcast_ref()
}
}
impl ClientHandler for ArcedClientHandler {
async fn run(&self, conn: &mut Conn) -> Result<()> {
self.0.run(conn).await
}
async fn after_response(&self, conn: &mut Conn) -> Result<()> {
self.0.after_response(conn).await
}
fn name(&self) -> Cow<'static, str> {
self.0.name()
}
}
impl ClientHandler for () {}
impl<H: ClientHandler> ClientHandler for Option<H> {
async fn run(&self, conn: &mut Conn) -> Result<()> {
if let Some(h) = self {
h.run(conn).await?;
}
Ok(())
}
async fn after_response(&self, conn: &mut Conn) -> Result<()> {
if let Some(h) = self {
h.after_response(conn).await?;
}
Ok(())
}
fn name(&self) -> Cow<'static, str> {
match self {
Some(h) => h.name(),
None => "None".into(),
}
}
}
macro_rules! reverse_after_response {
($conn:ident, $name:ident) => {
log::trace!("after_response {}", $name.name());
$name.after_response($conn).await?;
};
($conn:ident, $name:ident $($rest:ident)+) => {
reverse_after_response!($conn, $($rest)+);
log::trace!("after_response {}", $name.name());
$name.after_response($conn).await?;
};
}
macro_rules! impl_client_handler_tuple {
($($name:ident)+) => {
impl<$($name: ClientHandler),+> ClientHandler for ($($name,)+) {
#[allow(non_snake_case)]
async fn run(&self, conn: &mut Conn) -> Result<()> {
let ($(ref $name,)+) = *self;
$(
log::trace!("running {}", $name.name());
$name.run(conn).await?;
if conn.is_halted() {
return Ok(());
}
)+
Ok(())
}
#[allow(non_snake_case)]
async fn after_response(&self, conn: &mut Conn) -> Result<()> {
let ($(ref $name,)+) = *self;
reverse_after_response!(conn, $($name)+);
Ok(())
}
#[allow(non_snake_case)]
fn name(&self) -> Cow<'static, str> {
let ($(ref $name,)+) = *self;
format!(concat!("(\n", $(
concat!(" {",stringify!($name) ,":},\n")
),*, ")"), $($name = ($name).name()),*).into()
}
}
};
}
impl_client_handler_tuple! { A }
impl_client_handler_tuple! { A B }
impl_client_handler_tuple! { A B C }
impl_client_handler_tuple! { A B C D }
impl_client_handler_tuple! { A B C D E }
impl_client_handler_tuple! { A B C D E F }
impl_client_handler_tuple! { A B C D E F G }
impl_client_handler_tuple! { A B C D E F G H }
impl_client_handler_tuple! { A B C D E F G H I }
impl_client_handler_tuple! { A B C D E F G H I J }
impl_client_handler_tuple! { A B C D E F G H I J K }
impl_client_handler_tuple! { A B C D E F G H I J K L }
impl_client_handler_tuple! { A B C D E F G H I J K L M }
impl_client_handler_tuple! { A B C D E F G H I J K L M N }
impl_client_handler_tuple! { A B C D E F G H I J K L M N O }