From 4ca5d8bcfc7e5b32caeb8a585ec206f16f64329f Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 13:38:25 -0700 Subject: [PATCH] add FromRequest impl for tuples of various length --- src/extractor.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/src/extractor.rs b/src/extractor.rs index 12ae9e981..9d707e22f 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -1,17 +1,18 @@ +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::str; use bytes::Bytes; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; -use futures::future::Future; +use futures::{Async, Future, Poll}; use mime::Mime; use serde::de::{self, DeserializeOwned}; use serde_urlencoded; use de::PathDeserializer; use error::{Error, ErrorBadRequest}; -use handler::FromRequest; +use handler::{FromRequest, Reply}; use httpmessage::{HttpMessage, MessageBody, UrlEncoded}; use httprequest::HttpRequest; @@ -445,6 +446,123 @@ impl Default for PayloadConfig { } } +macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { + + /// FromRequest implementation for tuple + impl + 'static),+> FromRequest for ($($T,)+) + where + S: 'static, + { + type Config = ($($T::Config,)+); + type Result = Box>; + + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { + Box::new($fut_type { + s: PhantomData, + items: <($(Option<$T>,)+)>::default(), + futs: ($(Some($T::from_request(req, &cfg.$n).into()),)+), + }) + } + } + + struct $fut_type),+> + where + S: 'static, + { + s: PhantomData, + items: ($(Option<$T>,)+), + futs: ($(Option>,)+), + } + + impl),+> Future for $fut_type + where + S: 'static, + { + type Item = ($($T,)+); + type Error = Error; + + fn poll(&mut self) -> Poll { + let mut ready = true; + + $( + if self.futs.$n.is_some() { + match self.futs.$n.as_mut().unwrap().poll() { + Ok(Async::Ready(item)) => { + self.items.$n = Some(item); + self.futs.$n.take(); + } + Ok(Async::NotReady) => ready = false, + Err(e) => return Err(e), + } + } + )+ + + if ready { + Ok(Async::Ready( + ($(self.items.$n.take().unwrap(),)+) + )) + } else { + Ok(Async::NotReady) + } + } + } +}); + +tuple_from_req!(TupleFromRequest1, (0, A)); +tuple_from_req!(TupleFromRequest2, (0, A), (1, B)); +tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C)); +tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D)); +tuple_from_req!( + TupleFromRequest5, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E) +); +tuple_from_req!( + TupleFromRequest6, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F) +); +tuple_from_req!( + TupleFromRequest7, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F), + (6, G) +); +tuple_from_req!( + TupleFromRequest8, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F), + (6, G), + (7, H) +); +tuple_from_req!( + TupleFromRequest9, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F), + (6, G), + (7, H), + (8, I) +); + #[cfg(test)] mod tests { use super::*; @@ -609,4 +727,37 @@ mod tests { assert_eq!(*Path::::from_request(&mut req, &()).unwrap(), 32); } + + #[test] + fn test_tuple_extract() { + let mut req = TestRequest::with_uri("/name/user1/?id=test").finish(); + + let mut resource = ResourceHandler::<()>::default(); + resource.name("index"); + let mut routes = Vec::new(); + routes.push(( + Resource::new("index", "/{key}/{value}/"), + Some(resource), + )); + let (router, _) = Router::new("", ServerSettings::default(), routes); + assert!(router.recognize(&mut req).is_some()); + + let res = match <(Path<(String, String)>,)>::extract(&req).poll() { + Ok(Async::Ready(res)) => res, + _ => panic!("error"), + }; + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); + + let res = match <(Path<(String, String)>, Path<(String, String)>)>::extract(&req) + .poll() + { + Ok(Async::Ready(res)) => res, + _ => panic!("error"), + }; + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); + assert_eq!((res.1).0, "name"); + assert_eq!((res.1).1, "user1"); + } }