diff --git a/src/extract/mod.rs b/src/extract/mod.rs index e68c45b3..175ba36f 100644 --- a/src/extract/mod.rs +++ b/src/extract/mod.rs @@ -1,3 +1,5 @@ +//! Types and traits for extracting data from requests. + use crate::{body::Body, response::IntoResponse}; use async_trait::async_trait; use bytes::Bytes; @@ -275,7 +277,7 @@ macro_rules! impl_parse_url { }; } -impl_parse_url!(T1, T2, T3, T4, T5, T6); +impl_parse_url!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); fn take_body(req: &mut Request) -> Result { struct BodyAlreadyTakenExt; @@ -287,3 +289,30 @@ fn take_body(req: &mut Request) -> Result { Ok(body) } } + +macro_rules! impl_from_request_tuple { + () => {}; + + ( $head:ident, $($tail:ident),* $(,)? ) => { + #[allow(non_snake_case)] + #[async_trait] + impl FromRequest for ($head, $($tail,)*) + where + R: IntoResponse, + $head: FromRequest + Send, + $( $tail: FromRequest + Send, )* + { + type Rejection = R; + + async fn from_request(req: &mut Request) -> Result { + let $head = FromRequest::from_request(req).await?; + $( let $tail = FromRequest::from_request(req).await?; )* + Ok(($head, $($tail,)*)) + } + } + + impl_from_request_tuple!($($tail,)*); + }; +} + +impl_from_request_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); diff --git a/src/extract/rejection.rs b/src/extract/rejection.rs index ce85973b..49c48dd4 100644 --- a/src/extract/rejection.rs +++ b/src/extract/rejection.rs @@ -1,3 +1,5 @@ +//! Rejection response types. + use super::IntoResponse; use crate::body::Body; @@ -5,8 +7,10 @@ macro_rules! define_rejection { ( #[status = $status:ident] #[body = $body:expr] + $(#[$m:meta])* pub struct $name:ident (()); ) => { + $(#[$m])* #[derive(Debug)] pub struct $name(pub(super) ()); @@ -22,8 +26,10 @@ macro_rules! define_rejection { ( #[status = $status:ident] #[body = $body:expr] + $(#[$m:meta])* pub struct $name:ident (BoxError); ) => { + $(#[$m])* #[derive(Debug)] pub struct $name(pub(super) tower::BoxError); @@ -50,63 +56,84 @@ macro_rules! define_rejection { define_rejection! { #[status = BAD_REQUEST] #[body = "Query string was invalid or missing"] + /// Rejection type for [`Query`](super::Query). pub struct QueryStringMissing(()); } define_rejection! { #[status = BAD_REQUEST] #[body = "Failed to parse the response body as JSON"] + /// Rejection type for [`Json`](super::Json). pub struct InvalidJsonBody(BoxError); } define_rejection! { #[status = BAD_REQUEST] #[body = "Expected request with `Content-Type: application/json`"] + /// Rejection type for [`Json`](super::Json) used if the `Content-Type` + /// header is missing. pub struct MissingJsonContentType(()); } define_rejection! { #[status = INTERNAL_SERVER_ERROR] #[body = "Missing request extension"] + /// Rejection type for [`Extension`](super::Extension) if an expected + /// request extension was not found. pub struct MissingExtension(()); } define_rejection! { #[status = BAD_REQUEST] #[body = "Failed to buffer the request body"] + /// Rejection type for extractors that buffer the request body. Used if the + /// request body cannot be buffered due to an error. pub struct FailedToBufferBody(BoxError); } define_rejection! { #[status = BAD_REQUEST] #[body = "Response body didn't contain valid UTF-8"] + /// Rejection type used when buffering the request into a [`String`] if the + /// body doesn't contain valid UTF-8. pub struct InvalidUtf8(BoxError); } define_rejection! { #[status = PAYLOAD_TOO_LARGE] #[body = "Request payload is too large"] + /// Rejection type for [`BytesMaxLength`](super::BytesMaxLength) if the + /// request body is too large. pub struct PayloadTooLarge(()); } define_rejection! { #[status = LENGTH_REQUIRED] #[body = "Content length header is required"] + /// Rejection type for [`BytesMaxLength`](super::BytesMaxLength) if the + /// request is missing the `Content-Length` header or it is invalid. pub struct LengthRequired(()); } define_rejection! { #[status = INTERNAL_SERVER_ERROR] #[body = "No url params found for matched route. This is a bug in tower-web. Please open an issue"] + /// Rejection type for [`UrlParamsMap`](super::UrlParamsMap) and + /// [`UrlParams`](super::UrlParams) if you try and extract the URL params + /// more than once. pub struct MissingRouteParams(()); } define_rejection! { #[status = INTERNAL_SERVER_ERROR] #[body = "Cannot have two request body extractors for a single handler"] + /// Rejection type used if you try and extract the request body more than + /// once. pub struct BodyAlreadyTaken(()); } +/// Rejection type for [`UrlParams`](super::UrlParams) if the capture route +/// param didn't have the expected type. #[derive(Debug)] pub struct InvalidUrlParam { type_name: &'static str, diff --git a/src/handler.rs b/src/handler.rs index c8c3fd47..ab2b8c16 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,3 +1,5 @@ +//! Async functions that can be used to handle requests. + use crate::{ body::{Body, BoxBody}, extract::FromRequest, diff --git a/src/lib.rs b/src/lib.rs index 98806a50..d792bc30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ //! handle(Request) -> Response`. //! - Solid foundation. tower-web is built on top of tower and makes it easy to //! plug in any middleware from the [tower] and [tower-http] ecosystem. -//! - Focus on routing, extracing data from requests, and generating responses. +//! - Focus on routing, extracting data from requests, and generating responses. //! tower middleware can handle the rest. //! - Macro free core. Macro frameworks have their place but tower-web focuses //! on providing a core that is macro free. diff --git a/src/response.rs b/src/response.rs index 15d85b2e..b32504f6 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,3 +1,5 @@ +//! Types and traits for generating responses. + use crate::Body; use bytes::Bytes; use http::{header, HeaderMap, HeaderValue, Response, StatusCode}; diff --git a/src/routing.rs b/src/routing.rs index 47161fa3..bccfe978 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -1,3 +1,5 @@ +//! Routing between [`Service`]s. + use crate::{body::BoxBody, response::IntoResponse, ResultExt}; use bytes::Bytes; use futures_util::{future, ready}; @@ -465,7 +467,7 @@ where } } -/// Nest a group of routes (or [`Service`]) at some path. +/// Nest a group of routes (or a [`Service`]) at some path. /// /// This allows you to break your application into smaller pieces and compose /// them together. This will strip the matching prefix from the URL so the