mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into web/feat/convert-err-extractor
This commit is contained in:
commit
04c9020969
|
@ -9,11 +9,13 @@
|
||||||
- `HttpResponse` can now be used as a `Responder` with any body type. [#2567]
|
- `HttpResponse` can now be used as a `Responder` with any body type. [#2567]
|
||||||
- `Result` extractor wrapper can now convert error types. [#2581]
|
- `Result` extractor wrapper can now convert error types. [#2581]
|
||||||
- Associated types in `FromRequest` impl for `Option` and `Result` has changed. [#2581]
|
- Associated types in `FromRequest` impl for `Option` and `Result` has changed. [#2581]
|
||||||
|
- Maximim number of extractors has changed from 10 to 12. [#2582]
|
||||||
|
|
||||||
[#1988]: https://github.com/actix/actix-web/pull/1988
|
[#1988]: https://github.com/actix/actix-web/pull/1988
|
||||||
[#2567]: https://github.com/actix/actix-web/pull/2567
|
[#2567]: https://github.com/actix/actix-web/pull/2567
|
||||||
[#2569]: https://github.com/actix/actix-web/pull/2569
|
[#2569]: https://github.com/actix/actix-web/pull/2569
|
||||||
[#2581] https://github.com/actix/actix-web/pull/2581
|
[#2581] https://github.com/actix/actix-web/pull/2581
|
||||||
|
[#2582]: https://github.com/actix/actix-web/pull/2582
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.19 - 2022-01-04
|
## 4.0.0-beta.19 - 2022-01-04
|
||||||
|
|
|
@ -71,8 +71,8 @@ experimental-io-uring = ["actix-server/io-uring"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.1"
|
actix-codec = "0.4.1"
|
||||||
actix-macros = "0.2.3"
|
actix-macros = "0.2.3"
|
||||||
actix-rt = "2.3"
|
actix-rt = "2.6"
|
||||||
actix-server = "2.0.0-rc.2"
|
actix-server = "2.0.0-rc.4"
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3.0.0"
|
||||||
actix-tls = { version = "3.0.0", default-features = false, optional = true }
|
actix-tls = { version = "3.0.0", default-features = false, optional = true }
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
- The `prefer_utf8` option introduced in `0.4.0` is now true by default. [#2583]
|
||||||
|
|
||||||
|
[#2583]: https://github.com/actix/actix-web/pull/2583
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.13 - 2022-01-04
|
## 0.6.0-beta.13 - 2022-01-04
|
||||||
|
|
|
@ -39,7 +39,7 @@ mime_guess = "2.0.1"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
pin-project-lite = "0.2.7"
|
pin-project-lite = "0.2.7"
|
||||||
|
|
||||||
tokio-uring = { version = "0.1", optional = true }
|
tokio-uring = { version = "0.2", optional = true, features = ["bytes"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
|
|
|
@ -10,6 +10,9 @@ use actix_web::{error::Error, web::Bytes};
|
||||||
use futures_core::{ready, Stream};
|
use futures_core::{ready, Stream};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
|
||||||
|
#[cfg(feature = "experimental-io-uring")]
|
||||||
|
use bytes::BytesMut;
|
||||||
|
|
||||||
use super::named::File;
|
use super::named::File;
|
||||||
|
|
||||||
pin_project! {
|
pin_project! {
|
||||||
|
@ -214,64 +217,3 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "experimental-io-uring")]
|
|
||||||
use bytes_mut::BytesMut;
|
|
||||||
|
|
||||||
// TODO: remove new type and use bytes::BytesMut directly
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg(feature = "experimental-io-uring")]
|
|
||||||
mod bytes_mut {
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use tokio_uring::buf::{IoBuf, IoBufMut};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct BytesMut(bytes::BytesMut);
|
|
||||||
|
|
||||||
impl BytesMut {
|
|
||||||
pub(super) fn new() -> Self {
|
|
||||||
Self(bytes::BytesMut::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for BytesMut {
|
|
||||||
type Target = bytes::BytesMut;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for BytesMut {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl IoBuf for BytesMut {
|
|
||||||
fn stable_ptr(&self) -> *const u8 {
|
|
||||||
self.0.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bytes_init(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bytes_total(&self) -> usize {
|
|
||||||
self.0.capacity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl IoBufMut for BytesMut {
|
|
||||||
fn stable_mut_ptr(&mut self) -> *mut u8 {
|
|
||||||
self.0.as_mut_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn set_init(&mut self, init_len: usize) {
|
|
||||||
if self.len() < init_len {
|
|
||||||
self.0.set_len(init_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ mod tests {
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_service::ServiceFactory;
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
dev::ServiceFactory,
|
||||||
guard,
|
guard,
|
||||||
http::{
|
http::{
|
||||||
header::{self, ContentDisposition, DispositionParam, DispositionType},
|
header::{self, ContentDisposition, DispositionParam, DispositionType},
|
||||||
|
@ -303,7 +303,7 @@ mod tests {
|
||||||
let resp = file.respond_to(&req).await.unwrap();
|
let resp = file.respond_to(&req).await.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
"application/javascript"
|
"application/javascript; charset=utf-8"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
|
||||||
fs::Metadata,
|
fs::Metadata,
|
||||||
io,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_service::{Service, ServiceFactory};
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{self, BoxBody, SizedStream},
|
body::{self, BoxBody, SizedStream},
|
||||||
dev::{AppService, HttpServiceFactory, ResourceDef, ServiceRequest, ServiceResponse},
|
dev::{
|
||||||
|
self, AppService, HttpServiceFactory, ResourceDef, Service, ServiceFactory,
|
||||||
|
ServiceRequest, ServiceResponse,
|
||||||
|
},
|
||||||
http::{
|
http::{
|
||||||
header::{
|
header::{
|
||||||
self, Charset, ContentDisposition, ContentEncoding, DispositionParam,
|
self, Charset, ContentDisposition, ContentEncoding, DispositionParam,
|
||||||
|
@ -37,7 +38,7 @@ bitflags! {
|
||||||
|
|
||||||
impl Default for Flags {
|
impl Default for Flags {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Flags::from_bits_truncate(0b0000_0111)
|
Flags::from_bits_truncate(0b0000_1111)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +66,12 @@ impl Default for Flags {
|
||||||
/// NamedFile::open_async("./static/index.html").await
|
/// NamedFile::open_async("./static/index.html").await
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Deref, DerefMut)]
|
#[derive(Debug, Deref, DerefMut)]
|
||||||
pub struct NamedFile {
|
pub struct NamedFile {
|
||||||
path: PathBuf,
|
|
||||||
#[deref]
|
#[deref]
|
||||||
#[deref_mut]
|
#[deref_mut]
|
||||||
file: File,
|
file: File,
|
||||||
|
path: PathBuf,
|
||||||
modified: Option<SystemTime>,
|
modified: Option<SystemTime>,
|
||||||
pub(crate) md: Metadata,
|
pub(crate) md: Metadata,
|
||||||
pub(crate) flags: Flags,
|
pub(crate) flags: Flags,
|
||||||
|
@ -80,32 +81,6 @@ pub struct NamedFile {
|
||||||
pub(crate) encoding: Option<ContentEncoding>,
|
pub(crate) encoding: Option<ContentEncoding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for NamedFile {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("NamedFile")
|
|
||||||
.field("path", &self.path)
|
|
||||||
.field(
|
|
||||||
"file",
|
|
||||||
#[cfg(feature = "experimental-io-uring")]
|
|
||||||
{
|
|
||||||
&"tokio_uring::File"
|
|
||||||
},
|
|
||||||
#[cfg(not(feature = "experimental-io-uring"))]
|
|
||||||
{
|
|
||||||
&self.file
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.field("modified", &self.modified)
|
|
||||||
.field("md", &self.md)
|
|
||||||
.field("flags", &self.flags)
|
|
||||||
.field("status_code", &self.status_code)
|
|
||||||
.field("content_type", &self.content_type)
|
|
||||||
.field("content_disposition", &self.content_disposition)
|
|
||||||
.field("encoding", &self.encoding)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "experimental-io-uring"))]
|
#[cfg(not(feature = "experimental-io-uring"))]
|
||||||
pub(crate) use std::fs::File;
|
pub(crate) use std::fs::File;
|
||||||
#[cfg(feature = "experimental-io-uring")]
|
#[cfg(feature = "experimental-io-uring")]
|
||||||
|
@ -627,7 +602,7 @@ impl Service<ServiceRequest> for NamedFileService {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
actix_service::always_ready!();
|
dev::always_ready!();
|
||||||
|
|
||||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let (req, _) = req.into_parts();
|
let (req, _) = req.into_parts();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{fmt, io, ops::Deref, path::PathBuf, rc::Rc};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::BoxBody,
|
body::BoxBody,
|
||||||
dev::{Service, ServiceRequest, ServiceResponse},
|
dev::{self, Service, ServiceRequest, ServiceResponse},
|
||||||
error::Error,
|
error::Error,
|
||||||
guard::Guard,
|
guard::Guard,
|
||||||
http::{header, Method},
|
http::{header, Method},
|
||||||
|
@ -98,7 +98,7 @@ impl Service<ServiceRequest> for FilesService {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
actix_service::always_ready!();
|
dev::always_ready!();
|
||||||
|
|
||||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
let is_method_valid = if let Some(guard) = &self.guards {
|
let is_method_valid = if let Some(guard) = &self.guards {
|
||||||
|
|
|
@ -19,12 +19,12 @@ async fn test_utf8_file_contents() {
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res.headers().get(header::CONTENT_TYPE),
|
res.headers().get(header::CONTENT_TYPE),
|
||||||
Some(&HeaderValue::from_static("text/plain")),
|
Some(&HeaderValue::from_static("text/plain; charset=utf-8")),
|
||||||
);
|
);
|
||||||
|
|
||||||
// prefer UTF-8 encoding
|
// disable UTF-8 attribute
|
||||||
let srv =
|
let srv =
|
||||||
test::init_service(App::new().service(Files::new("/", "./tests").prefer_utf8(true)))
|
test::init_service(App::new().service(Files::new("/", "./tests").prefer_utf8(false)))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
||||||
|
@ -33,6 +33,6 @@ async fn test_utf8_file_contents() {
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res.headers().get(header::CONTENT_TYPE),
|
res.headers().get(header::CONTENT_TYPE),
|
||||||
Some(&HeaderValue::from_static("text/plain; charset=utf-8")),
|
Some(&HeaderValue::from_static("text/plain")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,16 +292,6 @@ impl FromRequest for Method {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
impl FromRequest for () {
|
|
||||||
type Error = Infallible;
|
|
||||||
type Future = Ready<Result<Self, Self::Error>>;
|
|
||||||
|
|
||||||
fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
|
|
||||||
ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
mod tuple_from_req {
|
mod tuple_from_req {
|
||||||
|
@ -390,6 +380,15 @@ mod tuple_from_req {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromRequest for () {
|
||||||
|
type Error = Infallible;
|
||||||
|
type Future = Ready<Result<Self, Self::Error>>;
|
||||||
|
|
||||||
|
fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||||
|
ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tuple_from_req! { TupleFromRequest1; A }
|
tuple_from_req! { TupleFromRequest1; A }
|
||||||
tuple_from_req! { TupleFromRequest2; A, B }
|
tuple_from_req! { TupleFromRequest2; A, B }
|
||||||
tuple_from_req! { TupleFromRequest3; A, B, C }
|
tuple_from_req! { TupleFromRequest3; A, B, C }
|
||||||
|
@ -400,6 +399,8 @@ mod tuple_from_req {
|
||||||
tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }
|
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! { 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! { 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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -12,17 +12,14 @@ use crate::{
|
||||||
/// # What Is A Request Handler
|
/// # What Is A Request Handler
|
||||||
/// A request handler has three requirements:
|
/// A request handler has three requirements:
|
||||||
/// 1. It is an async function (or a function/closure that returns an appropriate future);
|
/// 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
|
/// 1. The async function (or future) resolves to a type that can be converted into an
|
||||||
/// [`HttpResponse`] (i.e., it implements the [`Responder`] trait).
|
/// [`HttpResponse`] (i.e., it implements the [`Responder`] trait).
|
||||||
///
|
///
|
||||||
/// # Compiler Errors
|
/// # Compiler Errors
|
||||||
/// If you get the error `the trait Handler<_> is not implemented`, then your handler does not
|
/// If you get the error `the trait Handler<_> is not implemented`, then your handler does not
|
||||||
/// fulfill one or more of the above requirements.
|
/// fulfill the _first_ of the above requirements. Missing other requirements manifest as errors on
|
||||||
///
|
/// implementing [`FromRequest`] and [`Responder`], respectively.
|
||||||
/// 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.
|
|
||||||
///
|
///
|
||||||
/// # How Do Handlers Receive Variable Numbers Of Arguments
|
/// # How Do Handlers Receive Variable Numbers Of Arguments
|
||||||
/// Rest assured there is no macro magic here; it's just traits.
|
/// 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
|
/// This is the source code for the 2-parameter implementation of `Handler` to help illustrate the
|
||||||
/// bounds of the handler call after argument extraction:
|
/// bounds of the handler call after argument extraction:
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// impl<Func, Arg1, Arg2, R> Handler<(Arg1, Arg2), R> for Func
|
/// impl<Func, Arg1, Arg2, Fut> Handler<(Arg1, Arg2)> for Func
|
||||||
/// where
|
/// where
|
||||||
/// Func: Fn(Arg1, Arg2) -> R + Clone + 'static,
|
/// Func: Fn(Arg1, Arg2) -> Fut + Clone + 'static,
|
||||||
/// R: Future,
|
/// Fut: Future,
|
||||||
/// R::Output: Responder,
|
|
||||||
/// {
|
/// {
|
||||||
/// 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)
|
/// (self)(arg1, arg2)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
@ -76,7 +75,6 @@ use crate::{
|
||||||
///
|
///
|
||||||
/// [arity]: https://en.wikipedia.org/wiki/Arity
|
/// [arity]: https://en.wikipedia.org/wiki/Arity
|
||||||
/// [`from_request`]: FromRequest::from_request
|
/// [`from_request`]: FromRequest::from_request
|
||||||
/// [on_unimpl]: https://github.com/rust-lang/rust/issues/29628
|
|
||||||
pub trait Handler<Args>: Clone + 'static {
|
pub trait Handler<Args>: Clone + 'static {
|
||||||
type Output;
|
type Output;
|
||||||
type Future: Future<Output = Self::Output>;
|
type Future: Future<Output = Self::Output>;
|
||||||
|
@ -121,8 +119,9 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
macro_rules! factory_tuple ({ $($param:ident)* } => {
|
macro_rules! factory_tuple ({ $($param:ident)* } => {
|
||||||
impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func
|
impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func
|
||||||
where Func: Fn($($param),*) -> Fut + Clone + 'static,
|
where
|
||||||
Fut: Future,
|
Func: Fn($($param),*) -> Fut + Clone + 'static,
|
||||||
|
Fut: Future,
|
||||||
{
|
{
|
||||||
type Output = Fut::Output;
|
type Output = Fut::Output;
|
||||||
type Future = Fut;
|
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 }
|
||||||
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 }
|
||||||
factory_tuple! { A B C D E F G H I J K L }
|
factory_tuple! { A B C D E F G H I J K L }
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn assert_impl_handler<T: FromRequest>(_: impl Handler<T>) {}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue