diff --git a/CHANGES.md b/CHANGES.md index c379c1545..805030dfb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,10 +7,12 @@ ### Changed - `HttpResponse` can now be used as a `Responder` with any body type. [#2567] +- Maximim number of extractors has changed from 10 to 12. [#2582] [#1988]: https://github.com/actix/actix-web/pull/1988 [#2567]: https://github.com/actix/actix-web/pull/2567 [#2569]: https://github.com/actix/actix-web/pull/2569 +[#2582]: https://github.com/actix/actix-web/pull/2582 ## 4.0.0-beta.19 - 2022-01-04 diff --git a/src/extract.rs b/src/extract.rs index f74a0a54e..de1cdde0c 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -290,16 +290,6 @@ impl FromRequest for Method { } } -#[doc(hidden)] -impl FromRequest for () { - type Error = Infallible; - type Future = Ready>; - - fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(()) - } -} - #[doc(hidden)] #[allow(non_snake_case)] mod tuple_from_req { @@ -388,6 +378,15 @@ mod tuple_from_req { } } + impl FromRequest for () { + type Error = Infallible; + type Future = Ready>; + + fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future { + ok(()) + } + } + tuple_from_req! { TupleFromRequest1; A } tuple_from_req! { TupleFromRequest2; A, B } tuple_from_req! { TupleFromRequest3; A, B, C } @@ -398,6 +397,8 @@ mod tuple_from_req { tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H } tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I } tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J } + tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K } + tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L } } #[cfg(test)] diff --git a/src/handler.rs b/src/handler.rs index d458e22e1..7eb70ed25 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -12,17 +12,14 @@ use crate::{ /// # What Is A Request Handler /// A request handler has three requirements: /// 1. It is an async function (or a function/closure that returns an appropriate future); -/// 1. The function accepts zero or more parameters that implement [`FromRequest`]; +/// 1. The function parameters (up to 12) implement [`FromRequest`]; /// 1. The async function (or future) resolves to a type that can be converted into an /// [`HttpResponse`] (i.e., it implements the [`Responder`] trait). /// /// # Compiler Errors /// If you get the error `the trait Handler<_> is not implemented`, then your handler does not -/// fulfill one or more of the above requirements. -/// -/// Unfortunately we cannot provide a better compile error message (while keeping the trait's -/// flexibility) unless a stable alternative to [`#[rustc_on_unimplemented]`][on_unimpl] is added -/// to Rust. +/// fulfill the _first_ of the above requirements. Missing other requirements manifest as errors on +/// implementing [`FromRequest`] and [`Responder`], respectively. /// /// # How Do Handlers Receive Variable Numbers Of Arguments /// Rest assured there is no macro magic here; it's just traits. @@ -62,13 +59,15 @@ use crate::{ /// This is the source code for the 2-parameter implementation of `Handler` to help illustrate the /// bounds of the handler call after argument extraction: /// ```ignore -/// impl Handler<(Arg1, Arg2), R> for Func +/// impl Handler<(Arg1, Arg2)> for Func /// where -/// Func: Fn(Arg1, Arg2) -> R + Clone + 'static, -/// R: Future, -/// R::Output: Responder, +/// Func: Fn(Arg1, Arg2) -> Fut + Clone + 'static, +/// Fut: Future, /// { -/// fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> R { +/// type Output = Fut::Output; +/// type Future = Fut; +/// +/// fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> Self::Future { /// (self)(arg1, arg2) /// } /// } @@ -76,7 +75,6 @@ use crate::{ /// /// [arity]: https://en.wikipedia.org/wiki/Arity /// [`from_request`]: FromRequest::from_request -/// [on_unimpl]: https://github.com/rust-lang/rust/issues/29628 pub trait Handler: Clone + 'static { type Output; type Future: Future; @@ -121,8 +119,9 @@ where /// ``` macro_rules! factory_tuple ({ $($param:ident)* } => { impl Handler<($($param,)*)> for Func - where Func: Fn($($param),*) -> Fut + Clone + 'static, - Fut: Future, + where + Func: Fn($($param),*) -> Fut + Clone + 'static, + Fut: Future, { type Output = Fut::Output; type Future = Fut; @@ -148,3 +147,25 @@ factory_tuple! { A B C D E F G H I } factory_tuple! { A B C D E F G H I J } factory_tuple! { A B C D E F G H I J K } factory_tuple! { A B C D E F G H I J K L } + +#[cfg(test)] +mod tests { + use super::*; + + fn assert_impl_handler(_: impl Handler) {} + + #[test] + fn arg_number() { + async fn handler_min() {} + + #[rustfmt::skip] + #[allow(clippy::too_many_arguments, clippy::just_underscores_and_digits)] + async fn handler_max( + _01: (), _02: (), _03: (), _04: (), _05: (), _06: (), + _07: (), _08: (), _09: (), _10: (), _11: (), _12: (), + ) {} + + assert_impl_handler(handler_min); + assert_impl_handler(handler_max); + } +}