From bf3d4f4a40a274962506b6f00289399a00e267e4 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 30 May 2021 11:56:13 +0200 Subject: [PATCH] generic body almost kinda works --- src/lib.rs | 146 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 59 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0df5ff3a..e8e0d264 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -226,14 +226,14 @@ mod sealed { #[async_trait] pub trait Handler: Sized { - type ResponseBody; + type Response: IntoResponse; // This seals the trait. We cannot use the regular "sealed super trait" approach // due to coherence. #[doc(hidden)] type Sealed: sealed::HiddentTrait; - async fn call(self, req: Request) -> Result, Error>; + async fn call(self, req: Request) -> Result; fn layer(self, layer: L) -> Layered where @@ -243,17 +243,37 @@ pub trait Handler: Sized { } } +pub trait IntoResponse { + fn into_response(self) -> Response; +} + +impl IntoResponse for Response +where + B: Into, +{ + fn into_response(self) -> Response { + self.map(Into::into) + } +} + +impl IntoResponse for String { + fn into_response(self) -> Response { + Response::new(Body::from(self)) + } +} + #[async_trait] -impl Handler<()> for F +impl Handler<()> for F where F: Fn(Request) -> Fut + Send + Sync, - Fut: Future, Error>> + Send, + Fut: Future> + Send, + Res: IntoResponse, { - type ResponseBody = B; + type Response = Res; type Sealed = sealed::Hidden; - async fn call(self, req: Request) -> Result, Error> { + async fn call(self, req: Request) -> Result { self(req).await } } @@ -262,17 +282,18 @@ macro_rules! impl_handler { ( $head:ident $(,)? ) => { #[async_trait] #[allow(non_snake_case)] - impl Handler<($head,)> for F + impl Handler<($head,)> for F where F: Fn(Request, $head) -> Fut + Send + Sync, - Fut: Future, Error>> + Send, + Fut: Future> + Send, + Res: IntoResponse, $head: FromRequest + Send, { - type ResponseBody = B; + type Response = Res; type Sealed = sealed::Hidden; - async fn call(self, mut req: Request) -> Result, Error> { + async fn call(self, mut req: Request) -> Result { let $head = $head::from_request(&mut req).await?; let res = self(req, $head).await?; Ok(res) @@ -283,18 +304,19 @@ macro_rules! impl_handler { ( $head:ident, $($tail:ident),* $(,)? ) => { #[async_trait] #[allow(non_snake_case)] - impl Handler<($head, $($tail,)*)> for F + impl Handler<($head, $($tail,)*)> for F where F: Fn(Request, $head, $($tail,)*) -> Fut + Send + Sync, - Fut: Future, Error>> + Send, + Fut: Future> + Send, + Res: IntoResponse, $head: FromRequest + Send, $( $tail: FromRequest + Send, )* { - type ResponseBody = B; + type Response = Res; type Sealed = sealed::Hidden; - async fn call(self, mut req: Request) -> Result, Error> { + async fn call(self, mut req: Request) -> Result { let $head = $head::from_request(&mut req).await?; $( let $tail = $tail::from_request(&mut req).await?; @@ -325,17 +347,18 @@ where } #[async_trait] -impl Handler for Layered +impl Handler for Layered where - S: Service, Response = Response> + Send, + S: Service> + Send, + S::Response: IntoResponse, S::Error: Into, S::Future: Send, { - type ResponseBody = B; + type Response = S::Response; type Sealed = sealed::Hidden; - async fn call(self, req: Request) -> Result, Error> { + async fn call(self, req: Request) -> Result { self.svc .oneshot(req) .await @@ -380,10 +403,10 @@ where impl Service> for HandlerSvc where - H: Handler + Clone + 'static, - H::ResponseBody: 'static, + H: Handler + Clone + Send + 'static, + H::Response: 'static, { - type Response = Response; + type Response = Response; type Error = Error; type Future = future::BoxFuture<'static, Result>; @@ -394,7 +417,10 @@ where fn call(&mut self, req: Request) -> Self::Future { let handler = self.handler.clone(); - Box::pin(Handler::call(handler, req)) + Box::pin(async move { + let res = Handler::call(handler, req).await?; + Ok(res.into_response()) + }) } } @@ -794,44 +820,46 @@ mod tests { Ok(Response::new(Body::empty())) } - let app = - app() - // routes with functions - .at("/") - .get(root) - // routes with closures - .at("/users") - .get(|_: Request, pagination: Query| async { - let pagination = pagination.into_inner(); - assert_eq!(pagination.page, 1); - assert_eq!(pagination.per_page, 30); + async fn users_index( + _: Request, + pagination: Query, + ) -> Result { + let pagination = pagination.into_inner(); + assert_eq!(pagination.page, 1); + assert_eq!(pagination.per_page, 30); + Ok::<_, Error>("users#index".to_string()) + } - Ok::<_, Error>(Response::new(Body::from("users#index"))) - }) - .post( - |_: Request, - payload: Json, - _state: Extension>| async { - let payload = payload.into_inner(); - assert_eq!(payload.username, "bob"); - - Ok::<_, Error>(Response::new(Body::from("users#create"))) - }, - ) - // routes with a service - .at("/service") - .get_service(service_fn(root)) - // routes with layers applied - .at("/large-static-file") - .get( - large_static_file.layer( - ServiceBuilder::new() - .layer(TimeoutLayer::new(Duration::from_secs(30))) - .layer(CompressionLayer::new()) - .into_inner(), - ), - ) - .into_service(); + let app = app() + // routes with functions + .at("/") + .get(root) + // routes with closures + .at("/users") + .get(users_index) + .post( + |_: Request, + payload: Json, + _state: Extension>| async { + let payload = payload.into_inner(); + assert_eq!(payload.username, "bob"); + Ok::<_, Error>(Response::new(Body::from("users#create"))) + }, + ) + // routes with a service + .at("/service") + .get_service(service_fn(root)) + // routes with layers applied + .at("/large-static-file") + .get( + large_static_file.layer( + ServiceBuilder::new() + .layer(TimeoutLayer::new(Duration::from_secs(30))) + .layer(CompressionLayer::new()) + .into_inner(), + ), + ) + .into_service(); // state shared by all routes, could hold db connection etc struct State {}