From 0c44e4464151d736ea2806a1ff8321919e6b9aa1 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 30 Mar 2017 23:06:53 -0700 Subject: [PATCH] Use the `RawStr` type for all form raw strings. This is a breaking change. This commit introduces `RawStr` to forms. In particular, after this commit, the `&str` type no longer implements `FromFormValue`, and so it cannot be used as a field in forms. Instad, the `&RawStr` can be used. The `FormItems` iterator now returns an `(&RawStr, &RawStr)` pair. --- codegen/src/decorators/derive_form.rs | 27 ++++---- codegen/src/decorators/route.rs | 2 +- codegen/tests/run-pass/complete-decorator.rs | 4 +- codegen/tests/run-pass/derive_form.rs | 21 +++--- contrib/src/uuid.rs | 6 +- examples/extended_validation/src/main.rs | 18 ++--- examples/extended_validation/src/tests.rs | 8 +-- examples/form_kitchen_sink/src/main.rs | 9 +-- examples/forms/src/main.rs | 8 +-- examples/query_params/src/main.rs | 4 +- lib/src/lib.rs | 1 + lib/src/request/form/form_items.rs | 61 ++++++++++++----- lib/src/request/form/from_form_value.rs | 69 +++++++++++--------- lib/src/request/form/mod.rs | 8 ++- lib/src/rocket.rs | 9 +-- 15 files changed, 145 insertions(+), 110 deletions(-) diff --git a/codegen/src/decorators/derive_form.rs b/codegen/src/decorators/derive_form.rs index 13bc34f7..0c0408e7 100644 --- a/codegen/src/decorators/derive_form.rs +++ b/codegen/src/decorators/derive_form.rs @@ -68,7 +68,11 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, is_unsafe: false, supports_unions: false, span: span, - attributes: Vec::new(), + // We add this attribute because some `FromFormValue` implementations + // can't fail. This is indicated via the `!` type. Rust checks if a + // match is made with something of that type, and since we always emit + // an `Err` match, we'll get this lint warning. + attributes: vec![quote_attr!(ecx, #[allow(unreachable_code)])], path: ty::Path { path: vec!["rocket", "request", "FromForm"], lifetime: lifetime_var, @@ -178,7 +182,8 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct let id_str = ident_string.as_str(); arms.push(quote_tokens!(cx, $id_str => { - $ident = match ::rocket::request::FromFormValue::from_form_value(v) { + let r = ::rocket::http::RawStr::from_str(v); + $ident = match ::rocket::request::FromFormValue::from_form_value(r) { Ok(v) => Some(v), Err(e) => { println!(" => Error parsing form val '{}': {:?}", @@ -194,9 +199,9 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct // and use the $arms generated above. stmts.push(quote_stmt!(cx, for (k, v) in $arg { - match k { + match k.as_str() { $arms - field if field == "_method" => { + "_method" => { /* This is a Rocket-specific field. If the user hasn't asked * for it, just let it go by without error. This should stay * in sync with Rocket::preprocess. */ @@ -214,19 +219,13 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct // that each parameter actually is Some() or has a default value. let mut failure_conditions = vec![]; - // Start with `false` in case there are no fields. - failure_conditions.push(quote_tokens!(cx, false)); - for &(ref ident, ref ty) in (&fields_and_types).iter() { - // Pushing an "||" (or) between every condition. - failure_conditions.push(quote_tokens!(cx, ||)); - failure_conditions.push(quote_tokens!(cx, if $ident.is_none() && <$ty as ::rocket::request::FromFormValue>::default().is_none() { println!(" => '{}' did not parse.", stringify!($ident)); - true - } else { false } + $return_err_stmt; + } )); } @@ -245,9 +244,7 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct // the structure. let self_ident = substr.type_ident; let final_block = quote_block!(cx, { - if $failure_conditions { - $return_err_stmt; - } + $failure_conditions Ok($self_ident { $result_fields }) }); diff --git a/codegen/src/decorators/route.rs b/codegen/src/decorators/route.rs index e00e5fdd..fc562abb 100644 --- a/codegen/src/decorators/route.rs +++ b/codegen/src/decorators/route.rs @@ -81,7 +81,7 @@ impl RouteGenerateExt for RouteParams { Err(_) => return ::rocket::Outcome::Forward(_data) }; - if !items.exhausted() { + if !items.exhaust() { println!(" => The query string {:?} is malformed.", $form_string); return ::rocket::Outcome::Failure(::rocket::http::Status::BadRequest); } diff --git a/codegen/tests/run-pass/complete-decorator.rs b/codegen/tests/run-pass/complete-decorator.rs index 1819ec5e..5fcc75c3 100644 --- a/codegen/tests/run-pass/complete-decorator.rs +++ b/codegen/tests/run-pass/complete-decorator.rs @@ -3,12 +3,12 @@ extern crate rocket; -use rocket::http::Cookies; +use rocket::http::{Cookies, RawStr}; use rocket::request::Form; #[derive(FromForm)] struct User<'a> { - name: &'a str, + name: &'a RawStr, nickname: String, } diff --git a/codegen/tests/run-pass/derive_form.rs b/codegen/tests/run-pass/derive_form.rs index 993be36d..08c2756b 100644 --- a/codegen/tests/run-pass/derive_form.rs +++ b/codegen/tests/run-pass/derive_form.rs @@ -4,6 +4,7 @@ extern crate rocket; use rocket::request::{FromForm, FromFormValue, FormItems}; +use rocket::http::RawStr; #[derive(Debug, PartialEq, FromForm)] struct TodoTask { @@ -20,8 +21,8 @@ enum FormOption { impl<'v> FromFormValue<'v> for FormOption { type Error = &'v str; - fn from_form_value(v: &'v str) -> Result { - let variant = match v { + fn from_form_value(v: &'v RawStr) -> Result { + let variant = match v.as_str() { "a" => FormOption::A, "b" => FormOption::B, "c" => FormOption::C, @@ -37,19 +38,19 @@ struct FormInput<'r> { checkbox: bool, number: usize, radio: FormOption, - password: &'r str, + password: &'r RawStr, textarea: String, select: FormOption, } #[derive(Debug, PartialEq, FromForm)] struct DefaultInput<'r> { - arg: Option<&'r str>, + arg: Option<&'r RawStr>, } #[derive(Debug, PartialEq, FromForm)] struct ManualMethod<'r> { - _method: Option<&'r str>, + _method: Option<&'r RawStr>, done: bool } @@ -61,13 +62,13 @@ struct UnpresentCheckbox { #[derive(Debug, PartialEq, FromForm)] struct UnpresentCheckboxTwo<'r> { checkbox: bool, - something: &'r str + something: &'r RawStr } fn parse<'f, T: FromForm<'f>>(string: &'f str) -> Option { let mut items = FormItems::from(string); let result = T::from_form_items(items.by_ref()); - if !items.exhausted() { + if !items.exhaust() { panic!("Invalid form input."); } @@ -103,7 +104,7 @@ fn main() { checkbox: false, number: 10, radio: FormOption::C, - password: "testing", + password: "testing".into(), textarea: "".to_string(), select: FormOption::A, })); @@ -117,7 +118,7 @@ fn main() { // Ensure _method can be captured if desired. let manual: Option = parse("_method=put&done=true"); assert_eq!(manual, Some(ManualMethod { - _method: Some("put"), + _method: Some("put".into()), done: true })); @@ -138,6 +139,6 @@ fn main() { let manual: Option = parse("something=hello"); assert_eq!(manual, Some(UnpresentCheckboxTwo { checkbox: false, - something: "hello" + something: "hello".into() })); } diff --git a/contrib/src/uuid.rs b/contrib/src/uuid.rs index 795be11e..a12232b4 100644 --- a/contrib/src/uuid.rs +++ b/contrib/src/uuid.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use std::ops::Deref; use rocket::request::{FromParam, FromFormValue}; +use rocket::http::RawStr; pub use self::uuid_ext::ParseError as UuidParseError; @@ -89,11 +90,12 @@ impl<'a> FromParam<'a> for UUID { } impl<'v> FromFormValue<'v> for UUID { - type Error = &'v str; + type Error = &'v RawStr; /// A value is successfully parsed if `form_value` is a properly formatted /// UUID. Otherwise, the raw form value is returned. - fn from_form_value(form_value: &'v str) -> Result { + #[inline(always)] + fn from_form_value(form_value: &'v RawStr) -> Result { form_value.parse().map_err(|_| form_value) } } diff --git a/examples/extended_validation/src/main.rs b/examples/extended_validation/src/main.rs index 347ac9da..75963d59 100644 --- a/examples/extended_validation/src/main.rs +++ b/examples/extended_validation/src/main.rs @@ -4,11 +4,11 @@ extern crate rocket; mod files; -#[cfg(test)] -mod tests; +#[cfg(test)] mod tests; use rocket::response::Redirect; use rocket::request::{Form, FromFormValue}; +use rocket::http::RawStr; #[derive(Debug)] struct StrongPassword<'r>(&'r str); @@ -18,7 +18,7 @@ struct AdultAge(isize); #[derive(FromForm)] struct UserLogin<'r> { - username: &'r str, + username: &'r RawStr, password: Result, &'static str>, age: Result, } @@ -26,11 +26,11 @@ struct UserLogin<'r> { impl<'v> FromFormValue<'v> for StrongPassword<'v> { type Error = &'static str; - fn from_form_value(v: &'v str) -> Result { + fn from_form_value(v: &'v RawStr) -> Result { if v.len() < 8 { - Err("Too short!") + Err("too short!") } else { - Ok(StrongPassword(v)) + Ok(StrongPassword(v.as_str())) } } } @@ -38,15 +38,15 @@ impl<'v> FromFormValue<'v> for StrongPassword<'v> { impl<'v> FromFormValue<'v> for AdultAge { type Error = &'static str; - fn from_form_value(v: &'v str) -> Result { + fn from_form_value(v: &'v RawStr) -> Result { let age = match isize::from_form_value(v) { Ok(v) => v, - Err(_) => return Err("Age value is not a number."), + Err(_) => return Err("value is not a number."), }; match age > 20 { true => Ok(AdultAge(age)), - false => Err("Must be at least 21."), + false => Err("must be at least 21."), } } } diff --git a/examples/extended_validation/src/tests.rs b/examples/extended_validation/src/tests.rs index 46e21717..8d096be4 100644 --- a/examples/extended_validation/src/tests.rs +++ b/examples/extended_validation/src/tests.rs @@ -37,14 +37,14 @@ fn test_invalid_user() { #[test] fn test_invalid_password() { test_login("Sergio", "password1", "30", Status::Ok, "Wrong password!"); - test_login("Sergio", "ok", "30", Status::Ok, "Password is invalid: Too short!"); + test_login("Sergio", "ok", "30", Status::Ok, "Password is invalid: too short!"); } #[test] fn test_invalid_age() { - test_login("Sergio", "password", "20", Status::Ok, "Must be at least 21."); - test_login("Sergio", "password", "-100", Status::Ok, "Must be at least 21."); - test_login("Sergio", "password", "hi", Status::Ok, "Age value is not a number"); + test_login("Sergio", "password", "20", Status::Ok, "must be at least 21."); + test_login("Sergio", "password", "-100", Status::Ok, "must be at least 21."); + test_login("Sergio", "password", "hi", Status::Ok, "value is not a number"); } fn check_bad_form(form_str: &str, status: Status) { diff --git a/examples/form_kitchen_sink/src/main.rs b/examples/form_kitchen_sink/src/main.rs index 001c137f..9563b696 100644 --- a/examples/form_kitchen_sink/src/main.rs +++ b/examples/form_kitchen_sink/src/main.rs @@ -3,9 +3,10 @@ extern crate rocket; +use std::io; use rocket::request::{Form, FromFormValue}; use rocket::response::NamedFile; -use std::io; +use rocket::http::RawStr; // TODO: Make deriving `FromForm` for this enum possible. #[derive(Debug)] @@ -14,10 +15,10 @@ enum FormOption { } impl<'v> FromFormValue<'v> for FormOption { - type Error = &'v str; + type Error = &'v RawStr; - fn from_form_value(v: &'v str) -> Result { - let variant = match v { + fn from_form_value(v: &'v RawStr) -> Result { + let variant = match v.as_str() { "a" => FormOption::A, "b" => FormOption::B, "c" => FormOption::C, diff --git a/examples/forms/src/main.rs b/examples/forms/src/main.rs index 6ab3c601..5f7e4ed9 100644 --- a/examples/forms/src/main.rs +++ b/examples/forms/src/main.rs @@ -8,12 +8,13 @@ mod files; use rocket::request::Form; use rocket::response::Redirect; +use rocket::http::RawStr; #[derive(FromForm)] struct UserLogin<'r> { - username: &'r str, + username: &'r RawStr, password: String, - age: Result, + age: Result, } #[post("/login", data = "")] @@ -36,9 +37,8 @@ fn login<'a>(user_form: Form<'a, UserLogin<'a>>) -> Result { } } - #[get("/user/")] -fn user_page(username: &str) -> String { +fn user_page(username: String) -> String { format!("This is {}'s page.", username) } diff --git a/examples/query_params/src/main.rs b/examples/query_params/src/main.rs index 37059bf6..3c48a4bd 100644 --- a/examples/query_params/src/main.rs +++ b/examples/query_params/src/main.rs @@ -6,8 +6,8 @@ extern crate rocket; #[cfg(test)] mod tests; #[derive(FromForm)] -struct Person<'r> { - name: &'r str, +struct Person { + name: String, age: Option } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index c0b9f9c9..f69710f8 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -6,6 +6,7 @@ #![feature(type_ascription)] #![feature(lookup_host)] #![feature(plugin)] +#![feature(never_type)] #![plugin(pear_codegen)] diff --git a/lib/src/request/form/form_items.rs b/lib/src/request/form/form_items.rs index daa92650..ab26d0e4 100644 --- a/lib/src/request/form/form_items.rs +++ b/lib/src/request/form/form_items.rs @@ -1,17 +1,20 @@ use memchr::memchr2; +use http::RawStr; + /// Iterator over the key/value pairs of a given HTTP form string. /// /// **Note:** The returned key/value pairs are _not_ URL decoded. To URL decode -/// the raw strings, use `String::from_form_value`: +/// the raw strings, use the +/// [`url_decode`](/rocket/http/struct.RawStr.html#method.url_decode) method: /// /// ```rust /// use rocket::request::{FormItems, FromFormValue}; /// /// let form_string = "greeting=Hello%2C+Mark%21&username=jake%2Fother"; /// for (key, value) in FormItems::from(form_string) { -/// let decoded_value = String::from_form_value(value); -/// match key { +/// let decoded_value = value.url_decode(); +/// match key.as_str() { /// "greeting" => assert_eq!(decoded_value, Ok("Hello, Mark!".into())), /// "username" => assert_eq!(decoded_value, Ok("jake/other".into())), /// _ => unreachable!() @@ -26,7 +29,7 @@ use memchr::memchr2; /// for completion via the [completed](#method.completed) method, which returns /// `true` if the iterator parsed the entire string that was passed to it. The /// iterator can also attempt to parse any remaining contents via -/// [exhausted](#method.exhausted); this method returns `true` if exhaustion +/// [exhaust](#method.exhaust); this method returns `true` if exhaustion /// succeeded. /// /// This iterator guarantees that all valid form strings are parsed to @@ -57,13 +60,20 @@ use memchr::memchr2; /// /// let form_string = "greeting=hello&username=jake"; /// let mut items = FormItems::from(form_string); -/// assert_eq!(items.next(), Some(("greeting", "hello"))); -/// assert_eq!(items.next(), Some(("username", "jake"))); +/// +/// let next = items.next().unwrap(); +/// assert_eq!(next.0, "greeting"); +/// assert_eq!(next.1, "hello"); +/// +/// let next = items.next().unwrap(); +/// assert_eq!(next.0, "username"); +/// assert_eq!(next.1, "jake"); +/// /// assert_eq!(items.next(), None); /// assert!(items.completed()); /// ``` pub struct FormItems<'f> { - string: &'f str, + string: &'f RawStr, next_index: usize } @@ -117,7 +127,7 @@ impl<'f> FormItems<'f> { /// /// assert!(items.next().is_some()); /// assert_eq!(items.completed(), false); - /// assert_eq!(items.exhausted(), true); + /// assert_eq!(items.exhaust(), true); /// assert_eq!(items.completed(), true); /// ``` /// @@ -130,10 +140,11 @@ impl<'f> FormItems<'f> { /// /// assert!(items.next().is_some()); /// assert_eq!(items.completed(), false); - /// assert_eq!(items.exhausted(), false); + /// assert_eq!(items.exhaust(), false); /// assert_eq!(items.completed(), false); /// ``` - pub fn exhausted(&mut self) -> bool { + #[inline] + pub fn exhaust(&mut self) -> bool { while let Some(_) = self.next() { } self.completed() } @@ -167,15 +178,16 @@ impl<'f> FormItems<'f> { /// assert_eq!(items.inner_str(), form_string); /// ``` #[inline] - pub fn inner_str(&self) -> &'f str { + pub fn inner_str(&self) -> &'f RawStr { self.string } } -impl<'f> From<&'f str> for FormItems<'f> { +impl<'f> From<&'f RawStr> for FormItems<'f> { /// Returns an iterator over the key/value pairs in the /// `x-www-form-urlencoded` form `string`. - fn from(string: &'f str) -> FormItems<'f> { + #[inline(always)] + fn from(string: &'f RawStr) -> FormItems<'f> { FormItems { string: string, next_index: 0 @@ -183,8 +195,20 @@ impl<'f> From<&'f str> for FormItems<'f> { } } +impl<'f> From<&'f str> for FormItems<'f> { + /// Returns an iterator over the key/value pairs in the + /// `x-www-form-urlencoded` form `string`. + #[inline(always)] + fn from(string: &'f str) -> FormItems<'f> { + FormItems { + string: string.into(), + next_index: 0 + } + } +} + impl<'f> Iterator for FormItems<'f> { - type Item = (&'f str, &'f str); + type Item = (&'f RawStr, &'f RawStr); fn next(&mut self) -> Option { let s = &self.string[self.next_index..]; @@ -204,7 +228,7 @@ impl<'f> Iterator for FormItems<'f> { }; self.next_index += key.len() + 1 + consumed; - Some((key, value)) + Some((key.into(), value.into())) } } @@ -224,18 +248,19 @@ mod test { let (expected_key, actual_key) = (expected[i].0, results[i].0); let (expected_val, actual_val) = (expected[i].1, results[i].1); - assert!(expected_key == actual_key, + assert!(actual_key == expected_key, "key [{}] mismatch: expected {}, got {}", i, expected_key, actual_key); - assert!(expected_val == actual_val, + assert!(actual_val == expected_val, "val [{}] mismatch: expected {}, got {}", i, expected_val, actual_val); } } else { - assert!(!items.exhausted()); + assert!(!items.exhaust()); } }); + (@bad $string:expr) => (check_form!(@opt $string, None : Option<&[(&str, &str)]>)); ($string:expr, $expected:expr) => (check_form!(@opt $string, Some($expected))); } diff --git a/lib/src/request/form/from_form_value.rs b/lib/src/request/form/from_form_value.rs index 87c7310b..5e765b37 100644 --- a/lib/src/request/form/from_form_value.rs +++ b/lib/src/request/form/from_form_value.rs @@ -1,8 +1,7 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr}; use std::str::FromStr; -use error::Error; -use http::uri::URI; +use http::RawStr; /// Trait to create instance of some type from a form value; expected from field /// types in structs deriving `FromForm`. @@ -38,15 +37,16 @@ use http::uri::URI; /// following structure: /// /// ```rust +/// # use rocket::http::RawStr; /// # #[allow(dead_code)] /// struct Person<'r> { /// name: String, -/// age: Result +/// age: Result /// } /// ``` /// -/// The `Err` value in this case is `&str` since `u16::from_form_value` returns -/// a `Result`. +/// The `Err` value in this case is `&RawStr` since `u16::from_form_value` +/// returns a `Result`. /// /// # Provided Implementations /// @@ -67,7 +67,7 @@ use http::uri::URI; /// `"false"`, `"off"`, or not present. In any other case, the raw form /// value is returned in the `Err` value. /// -/// * **str** +/// * **&RawStr** /// /// _This implementation always returns successfully._ /// @@ -106,14 +106,15 @@ use http::uri::URI; /// /// ```rust /// use rocket::request::FromFormValue; +/// use rocket::http::RawStr; /// /// struct AdultAge(usize); /// /// impl<'v> FromFormValue<'v> for AdultAge { -/// type Error = &'v str; +/// type Error = &'v RawStr; /// -/// fn from_form_value(form_value: &'v str) -> Result { -/// match usize::from_form_value(form_value) { +/// fn from_form_value(form_value: &'v RawStr) -> Result { +/// match form_value.parse::() { /// Ok(age) if age >= 21 => Ok(AdultAge(age)), /// _ => Err(form_value), /// } @@ -141,50 +142,50 @@ pub trait FromFormValue<'v>: Sized { /// Parses an instance of `Self` from an HTTP form field value or returns an /// `Error` if one cannot be parsed. - fn from_form_value(form_value: &'v str) -> Result; + fn from_form_value(form_value: &'v RawStr) -> Result; /// Returns a default value to be used when the form field does not exist. /// If this returns `None`, then the field is required. Otherwise, this /// should return `Some(default_value)`. The default implementation simply /// returns `None`. + #[inline(always)] fn default() -> Option { None } } -impl<'v> FromFormValue<'v> for &'v str { - type Error = Error; +impl<'v> FromFormValue<'v> for &'v RawStr { + type Error = !; // This just gives the raw string. - fn from_form_value(v: &'v str) -> Result { + #[inline(always)] + fn from_form_value(v: &'v RawStr) -> Result { Ok(v) } } impl<'v> FromFormValue<'v> for String { - type Error = &'v str; + type Error = &'v RawStr; // This actually parses the value according to the standard. - fn from_form_value(v: &'v str) -> Result { - let replaced = v.replace("+", " "); - match URI::percent_decode(replaced.as_bytes()) { - Err(_) => Err(v), - Ok(string) => Ok(string.into_owned()) - } + #[inline(always)] + fn from_form_value(v: &'v RawStr) -> Result { + v.url_decode().map_err(|_| v) } } impl<'v> FromFormValue<'v> for bool { - type Error = &'v str; + type Error = &'v RawStr; - fn from_form_value(v: &'v str) -> Result { - match v { + fn from_form_value(v: &'v RawStr) -> Result { + match v.as_str() { "on" | "true" => Ok(true), "off" | "false" => Ok(false), _ => Err(v), } } + #[inline(always)] fn default() -> Option { Some(false) } @@ -193,9 +194,11 @@ impl<'v> FromFormValue<'v> for bool { macro_rules! impl_with_fromstr { ($($T:ident),+) => ($( impl<'v> FromFormValue<'v> for $T { - type Error = &'v str; - fn from_form_value(v: &'v str) -> Result { - $T::from_str(v).map_err(|_| v) + type Error = &'v RawStr; + + #[inline(always)] + fn from_form_value(v: &'v RawStr) -> Result { + $T::from_str(v.as_str()).map_err(|_| v) } } )+) @@ -205,29 +208,31 @@ impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr); impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option { - type Error = Error; + type Error = !; - fn from_form_value(v: &'v str) -> Result { + #[inline(always)] + fn from_form_value(v: &'v RawStr) -> Result { match T::from_form_value(v) { Ok(v) => Ok(Some(v)), Err(_) => Ok(None), } } + #[inline(always)] fn default() -> Option> { Some(None) } } -// TODO: Add more useful implementations (range, regex, etc.). +// // TODO: Add more useful implementations (range, regex, etc.). impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Result { - type Error = Error; + type Error = !; - fn from_form_value(v: &'v str) -> Result { + #[inline(always)] + fn from_form_value(v: &'v RawStr) -> Result { match T::from_form_value(v) { ok@Ok(_) => Ok(ok), e@Err(_) => Ok(e), } } } - diff --git a/lib/src/request/form/mod.rs b/lib/src/request/form/mod.rs index 57ccdf1a..edcef839 100644 --- a/lib/src/request/form/mod.rs +++ b/lib/src/request/form/mod.rs @@ -72,9 +72,10 @@ use outcome::Outcome::*; /// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; +/// # use rocket::http::RawStr; /// #[derive(FromForm)] /// struct UserInput<'f> { -/// value: &'f str +/// value: &'f RawStr /// } /// # fn main() { } /// ``` @@ -88,9 +89,10 @@ use outcome::Outcome::*; /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # use rocket::request::Form; +/// # use rocket::http::RawStr; /// # #[derive(FromForm)] /// # struct UserInput<'f> { -/// # value: &'f str +/// # value: &'f RawStr /// # } /// #[post("/submit", data = "")] /// fn submit_task<'r>(user_input: Form<'r, UserInput<'r>>) -> String { @@ -221,7 +223,7 @@ impl<'f, T: FromForm<'f> + 'f> Form<'f, T> { let mut items = FormItems::from(long_lived_string); let result = T::from_form_items(items.by_ref()); - if !items.exhausted() { + if !items.exhaust() { return FormResult::Invalid(form_string); } diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index c2ee2993..fe4fa4d5 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -168,10 +168,11 @@ impl Rocket { from_utf8_unchecked(&data.peek()[..min(data_len, max_len)]) }; - let mut form_items = FormItems::from(form); - if let Some(("_method", value)) = form_items.next() { - if let Ok(method) = value.parse() { - req.set_method(method); + if let Some((key, value)) = FormItems::from(form).next() { + if key == "_method" { + if let Ok(method) = value.parse() { + req.set_method(method); + } } } }