#![doc = include_str!("../docs/handlers_intro.md")]
use crate::{
body::{boxed, Body, Bytes, HttpBody},
extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
FromRequest, RequestParts,
},
response::{IntoResponse, Response},
routing::IntoMakeService,
BoxError,
};
use async_trait::async_trait;
use http::Request;
use std::{fmt, future::Future, marker::PhantomData};
use tower::ServiceExt;
use tower_layer::Layer;
use tower_service::Service;
pub mod future;
mod into_service;
pub use self::into_service::IntoService;
pub(crate) mod sealed {
#![allow(unreachable_pub, missing_docs, missing_debug_implementations)]
pub trait HiddenTrait {}
pub struct Hidden;
impl HiddenTrait for Hidden {}
}
#[async_trait]
pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
#[doc(hidden)]
type Sealed: sealed::HiddenTrait;
async fn call(self, req: Request<B>) -> Response;
fn layer<L>(self, layer: L) -> Layered<L::Service, T>
where
L: Layer<IntoService<Self, T, B>>,
{
Layered::new(layer.layer(self.into_service()))
}
fn into_service(self) -> IntoService<Self, T, B> {
IntoService::new(self)
}
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, B>> {
IntoMakeService::new(self.into_service())
}
fn into_make_service_with_connect_info<C, Target>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, B>, C>
where
C: Connected<Target>,
{
IntoMakeServiceWithConnectInfo::new(self.into_service())
}
}
#[async_trait]
impl<F, Fut, Res, B> Handler<(), B> for F
where
F: FnOnce() -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
B: Send + 'static,
{
type Sealed = sealed::Hidden;
async fn call(self, _req: Request<B>) -> Response {
self().await.into_response()
}
}
macro_rules! impl_handler {
( $($ty:ident),* $(,)? ) => {
#[async_trait]
#[allow(non_snake_case)]
impl<F, Fut, B, Res, $($ty,)*> Handler<($($ty,)*), B> for F
where
F: FnOnce($($ty,)*) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
B: Send + 'static,
Res: IntoResponse,
$( $ty: FromRequest<B> + Send,)*
{
type Sealed = sealed::Hidden;
async fn call(self, req: Request<B>) -> Response {
let mut req = RequestParts::new(req);
$(
let $ty = match $ty::from_request(&mut req).await {
Ok(value) => value,
Err(rejection) => return rejection.into_response(),
};
)*
let res = self($($ty,)*).await;
res.into_response()
}
}
};
}
all_the_tuples!(impl_handler);
pub struct Layered<S, T> {
svc: S,
_input: PhantomData<fn() -> T>,
}
impl<S, T> fmt::Debug for Layered<S, T>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Layered").field("svc", &self.svc).finish()
}
}
impl<S, T> Clone for Layered<S, T>
where
S: Clone,
{
fn clone(&self) -> Self {
Self::new(self.svc.clone())
}
}
#[async_trait]
impl<S, T, ReqBody, ResBody> Handler<T, ReqBody> for Layered<S, T>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Error: IntoResponse,
S::Future: Send,
T: 'static,
ReqBody: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>,
{
type Sealed = sealed::Hidden;
async fn call(self, req: Request<ReqBody>) -> Response {
match self.svc.oneshot(req).await {
Ok(res) => res.map(boxed),
Err(res) => res.into_response(),
}
}
}
impl<S, T> Layered<S, T> {
pub(crate) fn new(svc: S) -> Self {
Self {
svc,
_input: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::*;
use http::StatusCode;
#[tokio::test]
async fn handler_into_service() {
async fn handle(body: String) -> impl IntoResponse {
format!("you said: {}", body)
}
let client = TestClient::new(handle.into_service());
let res = client.post("/").body("hi there!").send().await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text().await, "you said: hi there!");
}
}