From 0099091e9664bc6d6e803ff5c1e1e236ce234a5e Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 23 Jul 2018 15:07:54 +0200 Subject: [PATCH 1/5] remove unnecessary use --- src/application.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/application.rs b/src/application.rs index 72ecff3da..f36adf69e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -610,7 +610,6 @@ impl Iterator for App { mod tests { use super::*; use body::{Binary, Body}; - use fs; use http::StatusCode; use httprequest::HttpRequest; use httpresponse::HttpResponse; From f4bb7efa89d4f21dfc6c7217fcb280f2b4d322d4 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 23 Jul 2018 15:10:30 +0200 Subject: [PATCH 2/5] add partialeq, eq, partialord and ord dervie to Path, Form and Query --- src/extractor.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/extractor.rs b/src/extractor.rs index 8e4745f86..7696e9920 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -16,7 +16,10 @@ use error::{Error, ErrorBadRequest, ErrorNotFound, UrlencodedError}; use handler::{AsyncResult, FromRequest}; use httpmessage::{HttpMessage, MessageBody, UrlEncoded}; use httprequest::HttpRequest; +use Result; +use futures::future; +#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's path. /// /// ## Example @@ -128,6 +131,7 @@ impl fmt::Display for Path { } } +#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from from the request's query. /// /// ## Example @@ -215,6 +219,7 @@ impl fmt::Display for Query { } } +#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's body. /// /// To extract typed information from request's body, the type `T` must From 1079c5c56202449a449b23ef39880b3816b2a385 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 23 Jul 2018 15:19:04 +0200 Subject: [PATCH 3/5] Add FromRequest implementation for Result and Option where T:FromRequest --- src/extractor.rs | 218 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/src/extractor.rs b/src/extractor.rs index 7696e9920..458b7f1a7 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -460,6 +460,126 @@ impl FromRequest for String { } } +/// Optionally extract a field from the request +/// +/// If the FromRequest for T fails, return None rather than returning an error response +/// +/// ## Example +/// +/// ```rust +/// # extern crate actix_web; +/// extern crate rand; +/// #[macro_use] extern crate serde_derive; +/// use actix_web::{http, App, Result, HttpRequest, Error, FromRequest}; +/// use actix_web::error::ErrorBadRequest; +/// +/// #[derive(Debug, Deserialize)] +/// struct Thing { name: String } +/// +/// impl FromRequest for Thing { +/// type Config = (); +/// type Result = Result; +/// +/// #[inline] +/// fn from_request(req: &HttpRequest, _cfg: &Self::Config) -> Self::Result { +/// if rand::random() { +/// Ok(Thing { name: "thingy".into() }) +/// } else { +/// Err(ErrorBadRequest("no luck")) +/// } +/// +/// } +/// } +/// +/// /// extract text data from request +/// fn index(supplied_thing: Option) -> Result { +/// match supplied_thing { +/// // Puns not intended +/// Some(thing) => Ok(format!("Got something: {:?}", thing)), +/// None => Ok(format!("No thing!")) +/// } +/// } +/// +/// fn main() { +/// let app = App::new().resource("/users/:first", |r| { +/// r.method(http::Method::POST).with(index) +/// }); +/// } +/// ``` +impl FromRequest for Option where T: FromRequest { + type Config = T::Config; + type Result = Box, Error = Error>>; + + #[inline] + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { + Box::new(T::from_request(req, cfg).into().then( |r| { + match r { + Ok(v) => future::ok(Some(v)), + Err(e) => { +// if true { panic!("{:?}", e.as_response_error()); } + + future::ok(None) + } + } + })) + } +} + +/// Optionally extract a field from the request or extract the Error if unsuccessful +/// +/// If the FromRequest for T fails, inject Err into handler rather than returning an error response +/// +/// ## Example +/// +/// ```rust +/// # extern crate actix_web; +/// extern crate rand; +/// #[macro_use] extern crate serde_derive; +/// use actix_web::{http, App, Result, HttpRequest, Error, FromRequest}; +/// use actix_web::error::ErrorBadRequest; +/// +/// #[derive(Debug, Deserialize)] +/// struct Thing { name: String } +/// +/// impl FromRequest for Thing { +/// type Config = (); +/// type Result = Result; +/// +/// #[inline] +/// fn from_request(req: &HttpRequest, _cfg: &Self::Config) -> Self::Result { +/// if rand::random() { +/// Ok(Thing { name: "thingy".into() }) +/// } else { +/// Err(ErrorBadRequest("no luck")) +/// } +/// +/// } +/// } +/// +/// /// extract text data from request +/// fn index(supplied_thing: Result) -> Result { +/// match supplied_thing { +/// Ok(thing) => Ok(format!("Got thing: {:?}", thing)), +/// Err(e) => Ok(format!("Error extracting thing: {}", e)) +/// } +/// } +/// +/// fn main() { +/// let app = App::new().resource("/users/:first", |r| { +/// r.method(http::Method::POST).with(index) +/// }); +/// } +/// ``` +impl FromRequest for Result where T: FromRequest{ + type Config = T::Config; + type Result = Box, Error = Error>>; + + #[inline] + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { + Box::new(T::from_request(req, cfg).into().then( |r| { future::ok(r) })) + } +} + /// Payload configuration for request's payload. pub struct PayloadConfig { limit: usize, @@ -685,6 +805,75 @@ mod tests { } } + #[test] + fn test_option() { + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).finish(); + + let mut cfg = FormConfig::default(); + cfg.limit(4096); + + match Option::>::from_request(&req, &cfg).poll().unwrap() { + Async::Ready(r) => assert_eq!(r, None), + _ => unreachable!(), + } + + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "9") + .set_payload(Bytes::from_static(b"hello=world")) + .finish(); + + match Option::>::from_request(&req, &cfg).poll().unwrap() { + Async::Ready(r) => assert_eq!(r, Some(Form(Info { hello: "world".into() }))), + _ => unreachable!(), + } + + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "9") + .set_payload(Bytes::from_static(b"bye=world")) + .finish(); + + match Option::>::from_request(&req, &cfg).poll().unwrap() { + Async::Ready(r) => assert_eq!(r, None), + _ => unreachable!(), + } + } + + #[test] + fn test_result() { + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) + .finish(); + + match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + Async::Ready(Ok(r)) => assert_eq!(r, Form(Info { hello: "world".into() })), + _ => unreachable!(), + } + + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "9") + .set_payload(Bytes::from_static(b"bye=world")) + .finish(); + + match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + Async::Ready(r) => assert!(r.is_err()), + _ => unreachable!(), + } + } + + + #[test] fn test_payload_config() { let req = TestRequest::default().finish(); @@ -797,4 +986,33 @@ mod tests { assert_eq!((res.1).0, "name"); assert_eq!((res.1).1, "user1"); } + +// #[test] +// fn test_tuple_optional() { +// let mut router = Router::<()>::new(); +// router.register_resource(Resource::new(ResourceDef::new("/{key}/{value}/"))); +// +// let req = TestRequest::with_uri("/name/?id=test").finish(); +// let info = router.recognize(&req, &(), 0); +// let req = req.with_route_info(info); +// +// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { +// Ok(res) => res, +// e => panic!("error {:?}", e), +// }; +// assert_eq!((res.0).0, Some("name".into())); +// assert_eq!((res.0).1, None); +// +// let req = TestRequest::with_uri("/user/?id=test").finish(); +// let info = router.recognize(&req, &(), 0); +// let req = req.with_route_info(info); +// +// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { +// Ok(res) => res, +// _ => panic!("error"), +// }; +// assert_eq!((res.0).0, None); +// assert_eq!((res.0).1, Some("user".into())); +// } + } From 35b754a3ab816637013436016bc801ac490c38f7 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Tue, 24 Jul 2018 09:39:27 +0200 Subject: [PATCH 4/5] pr fixes --- src/extractor.rs | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/src/extractor.rs b/src/extractor.rs index 458b7f1a7..768edfb76 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -6,7 +6,7 @@ use std::{fmt, str}; use bytes::Bytes; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; -use futures::{Async, Future, Poll}; +use futures::{Async, Future, Poll, future}; use mime::Mime; use serde::de::{self, DeserializeOwned}; use serde_urlencoded; @@ -16,8 +16,6 @@ use error::{Error, ErrorBadRequest, ErrorNotFound, UrlencodedError}; use handler::{AsyncResult, FromRequest}; use httpmessage::{HttpMessage, MessageBody, UrlEncoded}; use httprequest::HttpRequest; -use Result; -use futures::future; #[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's path. @@ -516,8 +514,6 @@ impl FromRequest for Option where T: FromRequest future::ok(Some(v)), Err(e) => { -// if true { panic!("{:?}", e.as_response_error()); } - future::ok(None) } } @@ -570,9 +566,9 @@ impl FromRequest for Option where T: FromRequest FromRequest for Result where T: FromRequest{ +impl FromRequest for Result where T: FromRequest{ type Config = T::Config; - type Result = Box, Error = Error>>; + type Result = Box, Error = Error>>; #[inline] fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { @@ -854,7 +850,7 @@ mod tests { .set_payload(Bytes::from_static(b"hello=world")) .finish(); - match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + match Result::, Error>::from_request(&req, &FormConfig::default()).poll().unwrap() { Async::Ready(Ok(r)) => assert_eq!(r, Form(Info { hello: "world".into() })), _ => unreachable!(), } @@ -866,7 +862,7 @@ mod tests { .set_payload(Bytes::from_static(b"bye=world")) .finish(); - match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + match Result::, Error>::from_request(&req, &FormConfig::default()).poll().unwrap() { Async::Ready(r) => assert!(r.is_err()), _ => unreachable!(), } @@ -986,33 +982,4 @@ mod tests { assert_eq!((res.1).0, "name"); assert_eq!((res.1).1, "user1"); } - -// #[test] -// fn test_tuple_optional() { -// let mut router = Router::<()>::new(); -// router.register_resource(Resource::new(ResourceDef::new("/{key}/{value}/"))); -// -// let req = TestRequest::with_uri("/name/?id=test").finish(); -// let info = router.recognize(&req, &(), 0); -// let req = req.with_route_info(info); -// -// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { -// Ok(res) => res, -// e => panic!("error {:?}", e), -// }; -// assert_eq!((res.0).0, Some("name".into())); -// assert_eq!((res.0).1, None); -// -// let req = TestRequest::with_uri("/user/?id=test").finish(); -// let info = router.recognize(&req, &(), 0); -// let req = req.with_route_info(info); -// -// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { -// Ok(res) => res, -// _ => panic!("error"), -// }; -// assert_eq!((res.0).0, None); -// assert_eq!((res.0).1, Some("user".into())); -// } - } From b48a2d4d7b36410297d3c3b3a6e3dffb665715d1 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Tue, 24 Jul 2018 22:25:48 +0200 Subject: [PATCH 5/5] add changes to CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 04c004fa7..15786fb69 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ## [0.7.1] - 2018-07-21 +### Added + + * Add implementation of `FromRequest` for `Option` and `Result` + ### Fixed * Fixed default_resource 'not yet implemented' panic #410