use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
use crate::http::StatusCode;
use crate::{Depot, FlowCtrl, Request, Response, async_trait};
#[async_trait]
pub trait Handler: Send + Sync + 'static {
#[doc(hidden)]
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
#[doc(hidden)]
fn type_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
#[must_use = "handle future must be used"]
async fn handle(
&self,
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
);
#[inline]
fn arc(self) -> ArcHandler
where
Self: Sized,
{
ArcHandler(Arc::new(self))
}
#[inline]
fn hooped<H: Handler>(self) -> HoopedHandler
where
Self: Sized,
{
HoopedHandler::new(self)
}
#[inline]
fn hoop<H: Handler>(self, hoop: H) -> HoopedHandler
where
Self: Sized,
{
HoopedHandler::new(self).hoop(hoop)
}
#[inline]
fn hoop_when<H, F>(self, hoop: H, filter: F) -> HoopedHandler
where
Self: Sized,
H: Handler,
F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
{
HoopedHandler::new(self).hoop_when(hoop, filter)
}
}
#[derive(Clone)]
pub struct ArcHandler(Arc<dyn Handler>);
impl Debug for ArcHandler {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("ArcHandler")
.field("inner", &self.0.type_name())
.finish()
}
}
#[async_trait]
impl Handler for ArcHandler {
async fn handle(
&self,
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
) {
self.0.handle(req, depot, res, ctrl).await
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct EmptyHandler;
#[async_trait]
impl Handler for EmptyHandler {
async fn handle(
&self,
_req: &mut Request,
_depot: &mut Depot,
res: &mut Response,
_ctrl: &mut FlowCtrl,
) {
res.status_code(StatusCode::OK);
}
}
#[must_use]
pub fn empty() -> EmptyHandler {
EmptyHandler
}
#[doc(hidden)]
#[non_exhaustive]
pub struct WhenHoop<H, F> {
pub inner: H,
pub filter: F,
}
impl<H: Debug, F: Debug> Debug for WhenHoop<H, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("WhenHoop")
.field("inner", &self.inner)
.field("filter", &self.filter)
.finish()
}
}
impl<H, F> WhenHoop<H, F> {
pub fn new(inner: H, filter: F) -> Self {
Self { inner, filter }
}
}
#[async_trait]
impl<H, F> Handler for WhenHoop<H, F>
where
H: Handler,
F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
{
async fn handle(
&self,
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
) {
if (self.filter)(req, depot) {
self.inner.handle(req, depot, res, ctrl).await;
} else {
ctrl.call_next(req, depot, res).await;
}
}
}
pub trait Skipper: Send + Sync + 'static {
fn skipped(&self, req: &mut Request, depot: &Depot) -> bool;
}
impl<F> Skipper for F
where
F: Fn(&mut Request, &Depot) -> bool + Send + Sync + 'static,
{
fn skipped(&self, req: &mut Request, depot: &Depot) -> bool {
(self)(req, depot)
}
}
#[non_exhaustive]
pub struct HoopedHandler {
inner: Arc<dyn Handler>,
hoops: Vec<Arc<dyn Handler>>,
}
impl Clone for HoopedHandler {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
hoops: self.hoops.clone(),
}
}
}
impl Debug for HoopedHandler {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("HoopedHandler")
.field("inner", &self.inner.type_name())
.field("hoops.len", &self.hoops.len())
.finish()
}
}
impl HoopedHandler {
pub fn new<H: Handler>(inner: H) -> Self {
Self {
inner: Arc::new(inner),
hoops: vec![],
}
}
#[inline]
#[must_use]
pub fn hoops(&self) -> &Vec<Arc<dyn Handler>> {
&self.hoops
}
#[inline]
pub fn hoops_mut(&mut self) -> &mut Vec<Arc<dyn Handler>> {
&mut self.hoops
}
#[inline]
#[must_use]
pub fn hoop<H: Handler>(mut self, hoop: H) -> Self {
self.hoops.push(Arc::new(hoop));
self
}
#[inline]
#[must_use]
pub fn hoop_when<H, F>(mut self, hoop: H, filter: F) -> Self
where
H: Handler,
F: Fn(&Request, &Depot) -> bool + Send + Sync + 'static,
{
self.hoops.push(Arc::new(WhenHoop::new(hoop, filter)));
self
}
}
#[async_trait]
impl Handler for HoopedHandler {
async fn handle(
&self,
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
) {
let inner: Arc<dyn Handler> = self.inner.clone();
let right = ctrl.handlers.split_off(ctrl.cursor);
ctrl.handlers.append(
&mut self
.hoops
.iter()
.cloned()
.chain([inner])
.chain(right)
.collect(),
);
ctrl.call_next(req, depot, res).await;
}
}
pub fn none_skipper(_req: &mut Request, _depot: &Depot) -> bool {
false
}
macro_rules! handler_tuple_impls {
($(
$Tuple:tt {
$(($idx:tt) -> $T:ident,)+
}
)+) => {$(
#[async_trait::async_trait]
impl<$($T,)+> Handler for ($($T,)+) where $($T: Handler,)+
{
async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
$(
if !res.is_stamped() {
self.$idx.handle(req, depot, res, ctrl).await;
}
)+
}
})+
}
}
macro_rules! skipper_tuple_impls {
($(
$Tuple:tt {
$(($idx:tt) -> $T:ident,)+
}
)+) => {$(
impl<$($T,)+> Skipper for ($($T,)+) where $($T: Skipper,)+
{
fn skipped(&self, req: &mut Request, depot: &Depot) -> bool {
$(
if self.$idx.skipped(req, depot) {
return true;
}
)+
false
}
})+
}
}
crate::for_each_tuple!(handler_tuple_impls);
crate::for_each_tuple!(skipper_tuple_impls);
#[cfg(test)]
mod tests {
use salvo_macros::handler;
use super::*;
use crate::Response;
use crate::http::StatusCode;
use crate::test::{ResponseExt, TestClient};
#[tokio::test]
async fn test_empty_handler() {
let res = TestClient::get("http://127.0.0.1:8698/")
.send(empty())
.await;
assert_eq!(res.status_code, Some(StatusCode::OK));
}
#[tokio::test]
async fn test_arc_handler() {
#[handler]
async fn hello(res: &mut Response) {
res.status_code(StatusCode::OK);
res.render("hello");
}
let mut res = TestClient::get("http://127.0.0.1:8698/")
.send(hello.arc())
.await;
assert_eq!(res.status_code, Some(StatusCode::OK));
assert_eq!(res.take_string().await.unwrap(), "hello");
}
}