mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-24 08:22:59 +01:00
Replace Pin::new_unchecked
with #[pin_project] in tuple_from_req!
(#1293)
Using some module trickery, we can generate a tuple struct for each invocation of the macro. This allows us to use `pin_project` to project through to the tuple fields, removing the need to use `Pin::new_unchecked` Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
This commit is contained in:
parent
a2d4ff157e
commit
d137a8635b
108
src/extract.rs
108
src/extract.rs
@ -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
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user