Only allow last extractor to mutate the request (#1272)

* Only allow last extractor to mutate the request

* Change `FromRequest` and add `FromRequestParts` trait (#1275)

* Add `Once`/`Mut` type parameter for `FromRequest` and `RequestParts`

* 🪄

* split traits

* `FromRequest` for tuples

* Remove `BodyAlreadyExtracted`

* don't need fully qualified path

* don't export `Once` and `Mut`

* remove temp tests

* depend on axum again

Co-authored-by: Jonas Platte <[email protected]>

* Port `Handler` and most extractors (#1277)

* Port `Handler` and most extractors

* Put `M` inside `Handler` impls, not trait itself

* comment out tuples for now

* fix lints

* Reorder arguments to `Handler` (#1281)

I think `Request<B>, Arc<S>` is better since its consistent with
`FromRequest` and `FromRequestParts`.

* Port most things in axum-extra (#1282)

* Port `#[derive(TypedPath)]` and `#[debug_handler]` (#1283)

* port #[derive(TypedPath)]

* wip: #[debug_handler]

* fix #[debug_handler]

* don't need itertools

* also require `Send`

* update expected error

* support fully qualified `self`

* Implement FromRequest[Parts] for tuples (#1286)

* Port docs for axum and axum-core (#1285)

* Port axum-extra (#1287)

* Port axum-extra

* Update axum-core/Cargo.toml

Co-authored-by: Jonas Platte <[email protected]>

* remove `impl FromRequest for Either*`

Co-authored-by: Jonas Platte <[email protected]>

* New FromRequest[Parts] trait cleanup (#1288)

* Make private module truly private again

* Simplify tuple FromRequest implementation

* Port `#[derive(FromRequest)]` (#1289)

* fix tests

* fix docs

* revert examples

* fix docs link

* fix intra docs links

* Port examples (#1291)

* Document wrapping other extractors (#1292)

* axum-extra doesn't need to depend on axum-core (#1294)

Missed this in https://github.com/tokio-rs/axum/pull/1287

* Add `FromRequest` changes to changelogs (#1293)

* Update changelog

* Remove default type for `S` in `Handler`

* Clarify which types have default types for `S`

* Apply suggestions from code review

Co-authored-by: Jonas Platte <[email protected]>

Co-authored-by: Jonas Platte <[email protected]>

* remove unused import

* Rename `Mut` and `Once` (#1296)

* fix trybuild expected output

Co-authored-by: Jonas Platte <[email protected]>
This commit is contained in:
David Pedersen
2022-08-22 12:23:20 +02:00
committed by GitHub
co-authored by Jonas Platte
parent f1769e5134
commit be624306f4
104 changed files with 1513 additions and 1936 deletions
+87 -225
View File
@@ -4,11 +4,10 @@
//!
//! [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
use self::rejection::*;
use crate::response::IntoResponse;
use async_trait::async_trait;
use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
use std::{convert::Infallible, sync::Arc};
use http::{request::Parts, Request};
use std::convert::Infallible;
pub mod rejection;
@@ -18,9 +17,44 @@ mod tuple;
pub use self::from_ref::FromRef;
mod private {
#[derive(Debug, Clone, Copy)]
pub enum ViaParts {}
#[derive(Debug, Clone, Copy)]
pub enum ViaRequest {}
}
/// Types that can be created from request parts.
///
/// Extractors that implement `FromRequestParts` cannot consume the request body and can thus be
/// run in any order for handlers.
///
/// If your extractor needs to consume the request body then you should implement [`FromRequest`]
/// and not [`FromRequestParts`].
///
/// See [`axum::extract`] for more general docs about extraxtors.
///
/// [`axum::extract`]: https://docs.rs/axum/0.6/axum/extract/index.html
#[async_trait]
pub trait FromRequestParts<S>: Sized {
/// If the extractor fails it'll use this "rejection" type. A rejection is
/// a kind of error that can be converted into a response.
type Rejection: IntoResponse;
/// Perform the extraction.
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection>;
}
/// Types that can be created from requests.
///
/// See [`axum::extract`] for more details.
/// Extractors that implement `FromRequest` can consume the request body and can thus only be run
/// once for handlers.
///
/// If your extractor doesn't need to consume the request body then you should implement
/// [`FromRequestParts`] and not [`FromRequest`].
///
/// See [`axum::extract`] for more general docs about extraxtors.
///
/// # What is the `B` type parameter?
///
@@ -39,7 +73,8 @@ pub use self::from_ref::FromRef;
/// ```rust
/// use axum::{
/// async_trait,
/// extract::{FromRequest, RequestParts},
/// extract::FromRequest,
/// http::Request,
/// };
///
/// struct MyExtractor;
@@ -48,12 +83,12 @@ pub use self::from_ref::FromRef;
/// impl<S, B> FromRequest<S, B> for MyExtractor
/// where
/// // these bounds are required by `async_trait`
/// B: Send,
/// B: Send + 'static,
/// S: Send + Sync,
/// {
/// type Rejection = http::StatusCode;
///
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
/// async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
/// // ...
/// # unimplemented!()
/// }
@@ -63,231 +98,45 @@ pub use self::from_ref::FromRef;
/// This ensures your extractor is as flexible as possible.
///
/// [`http::Request<B>`]: http::Request
/// [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
/// [`axum::extract`]: https://docs.rs/axum/0.6/axum/extract/index.html
#[async_trait]
pub trait FromRequest<S, B>: Sized {
pub trait FromRequest<S, B, M = private::ViaRequest>: Sized {
/// If the extractor fails it'll use this "rejection" type. A rejection is
/// a kind of error that can be converted into a response.
type Rejection: IntoResponse;
/// Perform the extraction.
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection>;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection>;
}
/// The type used with [`FromRequest`] to extract data from requests.
///
/// Has several convenience methods for getting owned parts of the request.
#[derive(Debug)]
pub struct RequestParts<S, B> {
pub(crate) state: Arc<S>,
method: Method,
uri: Uri,
version: Version,
headers: HeaderMap,
extensions: Extensions,
body: Option<B>,
}
#[async_trait]
impl<S, B, T> FromRequest<S, B, private::ViaParts> for T
where
B: Send + 'static,
S: Send + Sync,
T: FromRequestParts<S>,
{
type Rejection = <Self as FromRequestParts<S>>::Rejection;
impl<B> RequestParts<(), B> {
/// Create a new `RequestParts` without any state.
///
/// You generally shouldn't need to construct this type yourself, unless
/// using extractors outside of axum for example to implement a
/// [`tower::Service`].
///
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
pub fn new(req: Request<B>) -> Self {
Self::with_state((), req)
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
let (mut parts, _) = req.into_parts();
Self::from_request_parts(&mut parts, state).await
}
}
impl<S, B> RequestParts<S, B> {
/// Create a new `RequestParts` with the given state.
///
/// You generally shouldn't need to construct this type yourself, unless
/// using extractors outside of axum for example to implement a
/// [`tower::Service`].
///
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
pub fn with_state(state: S, req: Request<B>) -> Self {
Self::with_state_arc(Arc::new(state), req)
}
#[async_trait]
impl<S, T> FromRequestParts<S> for Option<T>
where
T: FromRequestParts<S>,
S: Send + Sync,
{
type Rejection = Infallible;
/// Create a new `RequestParts` with the given [`Arc`]'ed state.
///
/// You generally shouldn't need to construct this type yourself, unless
/// using extractors outside of axum for example to implement a
/// [`tower::Service`].
///
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
pub fn with_state_arc(state: Arc<S>, req: Request<B>) -> Self {
let (
http::request::Parts {
method,
uri,
version,
headers,
extensions,
..
},
body,
) = req.into_parts();
RequestParts {
state,
method,
uri,
version,
headers,
extensions,
body: Some(body),
}
}
/// Apply an extractor to this `RequestParts`.
///
/// `req.extract::<Extractor>()` is equivalent to `Extractor::from_request(req)`.
/// This function simply exists as a convenience.
///
/// # Example
///
/// ```
/// # struct MyExtractor {}
///
/// use std::convert::Infallible;
///
/// use async_trait::async_trait;
/// use axum::extract::{FromRequest, RequestParts};
/// use http::{Method, Uri};
///
/// #[async_trait]
/// impl<S, B> FromRequest<S, B> for MyExtractor
/// where
/// B: Send,
/// S: Send + Sync,
/// {
/// type Rejection = Infallible;
///
/// async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Infallible> {
/// let method = req.extract::<Method>().await?;
/// let path = req.extract::<Uri>().await?.path().to_owned();
///
/// todo!()
/// }
/// }
/// ```
pub async fn extract<E>(&mut self) -> Result<E, E::Rejection>
where
E: FromRequest<S, B>,
{
E::from_request(self).await
}
/// Convert this `RequestParts` back into a [`Request`].
///
/// Fails if The request body has been extracted, that is [`take_body`] has
/// been called.
///
/// [`take_body`]: RequestParts::take_body
pub fn try_into_request(self) -> Result<Request<B>, BodyAlreadyExtracted> {
let Self {
state: _,
method,
uri,
version,
headers,
extensions,
mut body,
} = self;
let mut req = if let Some(body) = body.take() {
Request::new(body)
} else {
return Err(BodyAlreadyExtracted);
};
*req.method_mut() = method;
*req.uri_mut() = uri;
*req.version_mut() = version;
*req.headers_mut() = headers;
*req.extensions_mut() = extensions;
Ok(req)
}
/// Gets a reference to the request method.
pub fn method(&self) -> &Method {
&self.method
}
/// Gets a mutable reference to the request method.
pub fn method_mut(&mut self) -> &mut Method {
&mut self.method
}
/// Gets a reference to the request URI.
pub fn uri(&self) -> &Uri {
&self.uri
}
/// Gets a mutable reference to the request URI.
pub fn uri_mut(&mut self) -> &mut Uri {
&mut self.uri
}
/// Get the request HTTP version.
pub fn version(&self) -> Version {
self.version
}
/// Gets a mutable reference to the request HTTP version.
pub fn version_mut(&mut self) -> &mut Version {
&mut self.version
}
/// Gets a reference to the request headers.
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
/// Gets a mutable reference to the request headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
/// Gets a reference to the request extensions.
pub fn extensions(&self) -> &Extensions {
&self.extensions
}
/// Gets a mutable reference to the request extensions.
pub fn extensions_mut(&mut self) -> &mut Extensions {
&mut self.extensions
}
/// Gets a reference to the request body.
///
/// Returns `None` if the body has been taken by another extractor.
pub fn body(&self) -> Option<&B> {
self.body.as_ref()
}
/// Gets a mutable reference to the request body.
///
/// Returns `None` if the body has been taken by another extractor.
// this returns `&mut Option<B>` rather than `Option<&mut B>` such that users can use it to set the body.
pub fn body_mut(&mut self) -> &mut Option<B> {
&mut self.body
}
/// Takes the body out of the request, leaving a `None` in its place.
pub fn take_body(&mut self) -> Option<B> {
self.body.take()
}
/// Get a reference to the state.
pub fn state(&self) -> &S {
&self.state
async fn from_request_parts(
parts: &mut Parts,
state: &S,
) -> Result<Option<T>, Self::Rejection> {
Ok(T::from_request_parts(parts, state).await.ok())
}
}
@@ -295,13 +144,26 @@ impl<S, B> RequestParts<S, B> {
impl<S, T, B> FromRequest<S, B> for Option<T>
where
T: FromRequest<S, B>,
B: Send,
B: Send + 'static,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Option<T>, Self::Rejection> {
Ok(T::from_request(req).await.ok())
async fn from_request(req: Request<B>, state: &S) -> Result<Option<T>, Self::Rejection> {
Ok(T::from_request(req, state).await.ok())
}
}
#[async_trait]
impl<S, T> FromRequestParts<S> for Result<T, T::Rejection>
where
T: FromRequestParts<S>,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
Ok(T::from_request_parts(parts, state).await)
}
}
@@ -309,12 +171,12 @@ where
impl<S, T, B> FromRequest<S, B> for Result<T, T::Rejection>
where
T: FromRequest<S, B>,
B: Send,
B: Send + 'static,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(T::from_request(req).await)
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
Ok(T::from_request(req, state).await)
}
}
+1 -32
View File
@@ -1,35 +1,6 @@
//! Rejection response types.
use crate::{
response::{IntoResponse, Response},
BoxError,
};
use http::StatusCode;
use std::fmt;
/// Rejection type used if you try and extract the request body more than
/// once.
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct BodyAlreadyExtracted;
impl BodyAlreadyExtracted {
const BODY: &'static str = "Cannot have two request body extractors for a single handler";
}
impl IntoResponse for BodyAlreadyExtracted {
fn into_response(self) -> Response {
(StatusCode::INTERNAL_SERVER_ERROR, Self::BODY).into_response()
}
}
impl fmt::Display for BodyAlreadyExtracted {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Self::BODY)
}
}
impl std::error::Error for BodyAlreadyExtracted {}
use crate::BoxError;
composite_rejection! {
/// Rejection type for extractors that buffer the request body. Used if the
@@ -85,7 +56,6 @@ composite_rejection! {
/// Contains one variant for each way the [`Bytes`](bytes::Bytes) extractor
/// can fail.
pub enum BytesRejection {
BodyAlreadyExtracted,
FailedToBufferBody,
}
}
@@ -95,7 +65,6 @@ composite_rejection! {
///
/// Contains one variant for each way the [`String`] extractor can fail.
pub enum StringRejection {
BodyAlreadyExtracted,
FailedToBufferBody,
InvalidUtf8,
}
+28 -71
View File
@@ -1,9 +1,9 @@
use super::{rejection::*, FromRequest, RequestParts};
use super::{rejection::*, FromRequest, FromRequestParts};
use crate::BoxError;
use async_trait::async_trait;
use bytes::Bytes;
use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
use std::{convert::Infallible, sync::Arc};
use http::{request::Parts, HeaderMap, Method, Request, Uri, Version};
use std::convert::Infallible;
#[async_trait]
impl<S, B> FromRequest<S, B> for Request<B>
@@ -11,62 +11,46 @@ where
B: Send,
S: Send + Sync,
{
type Rejection = BodyAlreadyExtracted;
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let req = std::mem::replace(
req,
RequestParts {
state: Arc::clone(&req.state),
method: req.method.clone(),
version: req.version,
uri: req.uri.clone(),
headers: HeaderMap::new(),
extensions: Extensions::default(),
body: None,
},
);
req.try_into_request()
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
Ok(req)
}
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Method
impl<S> FromRequestParts<S> for Method
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.method().clone())
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
Ok(parts.method.clone())
}
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Uri
impl<S> FromRequestParts<S> for Uri
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.uri().clone())
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
Ok(parts.uri.clone())
}
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Version
impl<S> FromRequestParts<S> for Version
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.version())
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
Ok(parts.version)
}
}
@@ -76,30 +60,29 @@ where
///
/// [`TypedHeader`]: https://docs.rs/axum/latest/axum/extract/struct.TypedHeader.html
#[async_trait]
impl<S, B> FromRequest<S, B> for HeaderMap
impl<S> FromRequestParts<S> for HeaderMap
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.headers().clone())
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
Ok(parts.headers.clone())
}
}
#[async_trait]
impl<S, B> FromRequest<S, B> for Bytes
where
B: http_body::Body + Send,
B: http_body::Body + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = BytesRejection;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?;
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
let body = req.into_body();
let bytes = crate::body::to_bytes(body)
.await
@@ -112,15 +95,15 @@ where
#[async_trait]
impl<S, B> FromRequest<S, B> for String
where
B: http_body::Body + Send,
B: http_body::Body + Send + 'static,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send + Sync,
{
type Rejection = StringRejection;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?;
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
let body = req.into_body();
let bytes = crate::body::to_bytes(body)
.await
@@ -134,40 +117,14 @@ where
}
#[async_trait]
impl<S, B> FromRequest<S, B> for http::request::Parts
impl<S, B> FromRequest<S, B> for Parts
where
B: Send,
B: Send + 'static,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let method = unwrap_infallible(Method::from_request(req).await);
let uri = unwrap_infallible(Uri::from_request(req).await);
let version = unwrap_infallible(Version::from_request(req).await);
let headers = unwrap_infallible(HeaderMap::from_request(req).await);
let extensions = std::mem::take(req.extensions_mut());
let mut temp_request = Request::new(());
*temp_request.method_mut() = method;
*temp_request.uri_mut() = uri;
*temp_request.version_mut() = version;
*temp_request.headers_mut() = headers;
*temp_request.extensions_mut() = extensions;
let (parts, _) = temp_request.into_parts();
Ok(parts)
async fn from_request(req: Request<B>, _: &S) -> Result<Self, Self::Rejection> {
Ok(req.into_parts().0)
}
}
fn unwrap_infallible<T>(result: Result<T, Infallible>) -> T {
match result {
Ok(value) => value,
Err(err) => match err {},
}
}
pub(crate) fn take_body<S, B>(req: &mut RequestParts<S, B>) -> Result<B, BodyAlreadyExtracted> {
req.take_body().ok_or(BodyAlreadyExtracted)
}
+117 -15
View File
@@ -1,41 +1,143 @@
use super::{FromRequest, RequestParts};
use super::{FromRequest, FromRequestParts};
use crate::response::{IntoResponse, Response};
use async_trait::async_trait;
use http::request::{Parts, Request};
use std::convert::Infallible;
#[async_trait]
impl<S, B> FromRequest<S, B> for ()
impl<S> FromRequestParts<S> for ()
where
B: Send,
S: Send + Sync,
{
type Rejection = Infallible;
async fn from_request(_: &mut RequestParts<S, B>) -> Result<(), Self::Rejection> {
async fn from_request_parts(_: &mut Parts, _: &S) -> Result<(), Self::Rejection> {
Ok(())
}
}
macro_rules! impl_from_request {
() => {};
( $($ty:ident),* $(,)? ) => {
(
[$($ty:ident),*], $last:ident
) => {
#[async_trait]
#[allow(non_snake_case)]
impl<S, B, $($ty,)*> FromRequest<S, B> for ($($ty,)*)
#[allow(non_snake_case, unused_mut, unused_variables)]
impl<S, $($ty,)* $last> FromRequestParts<S> for ($($ty,)* $last,)
where
$( $ty: FromRequest<S, B> + Send, )*
B: Send,
$( $ty: FromRequestParts<S> + Send, )*
$last: FromRequestParts<S> + Send,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
$( let $ty = $ty::from_request(req).await.map_err(|err| err.into_response())?; )*
Ok(($($ty,)*))
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
$(
let $ty = $ty::from_request_parts(parts, state)
.await
.map_err(|err| err.into_response())?;
)*
let $last = $last::from_request_parts(parts, state)
.await
.map_err(|err| err.into_response())?;
Ok(($($ty,)* $last,))
}
}
// This impl must not be generic over M, otherwise it would conflict with the blanket
// implementation of `FromRequest<S, B, Mut>` for `T: FromRequestParts<S>`.
#[async_trait]
#[allow(non_snake_case, unused_mut, unused_variables)]
impl<S, B, $($ty,)* $last> FromRequest<S, B> for ($($ty,)* $last,)
where
$( $ty: FromRequestParts<S> + Send, )*
$last: FromRequest<S, B> + Send,
B: Send + 'static,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
let (mut parts, body) = req.into_parts();
$(
let $ty = $ty::from_request_parts(&mut parts, state).await.map_err(|err| err.into_response())?;
)*
let req = Request::from_parts(parts, body);
let $last = $last::from_request(req, state).await.map_err(|err| err.into_response())?;
Ok(($($ty,)* $last,))
}
}
};
}
all_the_tuples!(impl_from_request);
impl_from_request!([], T1);
impl_from_request!([T1], T2);
impl_from_request!([T1, T2], T3);
impl_from_request!([T1, T2, T3], T4);
impl_from_request!([T1, T2, T3, T4], T5);
impl_from_request!([T1, T2, T3, T4, T5], T6);
impl_from_request!([T1, T2, T3, T4, T5, T6], T7);
impl_from_request!([T1, T2, T3, T4, T5, T6, T7], T8);
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
impl_from_request!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
impl_from_request!(
[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13],
T14
);
impl_from_request!(
[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14],
T15
);
impl_from_request!(
[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15],
T16
);
#[cfg(test)]
mod tests {
use bytes::Bytes;
use http::Method;
use crate::extract::{FromRequest, FromRequestParts};
fn assert_from_request<M, T>()
where
T: FromRequest<(), http_body::Full<Bytes>, M>,
{
}
fn assert_from_request_parts<T: FromRequestParts<()>>() {}
#[test]
fn unit() {
assert_from_request_parts::<()>();
assert_from_request::<_, ()>();
}
#[test]
fn tuple_of_one() {
assert_from_request_parts::<(Method,)>();
assert_from_request::<_, (Method,)>();
assert_from_request::<_, (Bytes,)>();
}
#[test]
fn tuple_of_two() {
assert_from_request_parts::<((), ())>();
assert_from_request::<_, ((), ())>();
assert_from_request::<_, (Method, Bytes)>();
}
#[test]
fn nested_tuple() {
assert_from_request_parts::<(((Method,),),)>();
assert_from_request::<_, ((((Bytes,),),),)>();
}
}