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