Merge branch 'master' into less-pin-unchecked

This commit is contained in:
Yuki Okushi 2020-01-28 11:09:22 +09:00 committed by GitHub
commit e7ab663d39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 45 deletions

View File

@ -193,57 +193,83 @@ impl FromRequest for () {
macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {
/// FromRequest implementation for tuple // This module is a trick to get around the inability of
#[doc(hidden)] // `macro_rules!` macros to make new idents. We want to make
#[allow(unused_parens)] // a new `FutWrapper` struct for each distinct invocation of
impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+) // this macro. Ideally, we would name it something like
{ // `FutWrapper_$fut_type`, but this can't be done in a macro_rules
type Error = Error; // macro.
type Future = $fut_type<$($T),+>; //
type Config = ($($T::Config),+); // Instead, we put everything in a module named `$fut_type`, thus allowing
// us to use the name `FutWrapper` without worrying about conflicts.
// This macro only exists to generate trait impls for tuples - these
// are inherently global, so users don't have to care about this
// weird trick.
#[allow(non_snake_case)]
mod $fut_type {
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { // Bring everything into scope, so we don't need
$fut_type { // redundant imports
items: <($(Option<$T>,)+)>::default(), use super::*;
futs: ($($T::from_request(req, payload),)+),
/// A helper struct to allow us to pin-project through
/// to individual fields
#[pin_project::pin_project]
struct FutWrapper<$($T: FromRequest),+>($(#[pin] $T::Future),+);
/// FromRequest implementation for tuple
#[doc(hidden)]
#[allow(unused_parens)]
impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
{
type Error = Error;
type Future = $fut_type<$($T),+>;
type Config = ($($T::Config),+);
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
$fut_type {
items: <($(Option<$T>,)+)>::default(),
futs: FutWrapper($($T::from_request(req, payload),)+),
}
} }
} }
}
#[doc(hidden)] #[doc(hidden)]
#[pin_project::pin_project] #[pin_project::pin_project]
pub struct $fut_type<$($T: FromRequest),+> { pub struct $fut_type<$($T: FromRequest),+> {
items: ($(Option<$T>,)+), items: ($(Option<$T>,)+),
futs: ($($T::Future,)+), #[pin]
} futs: FutWrapper<$($T,)+>,
}
impl<$($T: FromRequest),+> Future for $fut_type<$($T),+> impl<$($T: FromRequest),+> Future for $fut_type<$($T),+>
{ {
type Output = Result<($($T,)+), Error>; type Output = Result<($($T,)+), Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let mut this = self.project();
let mut ready = true; let mut ready = true;
$( $(
if this.items.$n.is_none() { if this.items.$n.is_none() {
match unsafe { Pin::new_unchecked(&mut this.futs.$n) }.poll(cx) { match this.futs.as_mut().project().$n.poll(cx) {
Poll::Ready(Ok(item)) => { Poll::Ready(Ok(item)) => {
this.items.$n = Some(item); this.items.$n = Some(item);
}
Poll::Pending => ready = false,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
} }
Poll::Pending => ready = false,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())),
} }
} )+
)+
if ready { if ready {
Poll::Ready(Ok( Poll::Ready(Ok(
($(this.items.$n.take().unwrap(),)+) ($(this.items.$n.take().unwrap(),)+)
)) ))
} else { } else {
Poll::Pending Poll::Pending
} }
}
} }
} }
}); });

View File

@ -95,11 +95,10 @@ where
/// Calls service and waits for response future completion. /// Calls service and waits for response future completion.
/// ///
/// ```rust /// ```rust
/// use actix_web::{test, App, HttpResponse, http::StatusCode}; /// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
/// use actix_service::Service;
/// ///
/// #[test] /// #[actix_rt::test]
/// fn test_response() { /// async fn test_response() {
/// let mut app = test::init_service( /// let mut app = test::init_service(
/// App::new() /// App::new()
/// .service(web::resource("/test").to(|| async { /// .service(web::resource("/test").to(|| async {