diff --git a/codegen/tests/run-pass/complete-decorator.rs b/codegen/tests/run-pass/complete-decorator.rs index 5fcc75c3..26c7a7d9 100644 --- a/codegen/tests/run-pass/complete-decorator.rs +++ b/codegen/tests/run-pass/complete-decorator.rs @@ -13,7 +13,7 @@ struct User<'a> { } #[post("/?", format = "application/json", data = "", rank = 2)] -fn get<'r>(name: &str, +fn get<'r>(name: &RawStr, query: User<'r>, user: Form<'r, User<'r>>, cookies: Cookies) diff --git a/codegen/tests/run-pass/dynamic-paths.rs b/codegen/tests/run-pass/dynamic-paths.rs index 92c331c6..b5f3a9c8 100644 --- a/codegen/tests/run-pass/dynamic-paths.rs +++ b/codegen/tests/run-pass/dynamic-paths.rs @@ -4,7 +4,7 @@ extern crate rocket; #[get("/test///")] -fn get(one: &str, two: usize, three: isize) -> &'static str { "hi" } +fn get(one: String, two: usize, three: isize) -> &'static str { "hi" } fn main() { let _ = routes![get]; diff --git a/codegen/tests/run-pass/issue-1-colliding-names.rs b/codegen/tests/run-pass/issue-1-colliding-names.rs index 284cf4d6..6c97c895 100644 --- a/codegen/tests/run-pass/issue-1-colliding-names.rs +++ b/codegen/tests/run-pass/issue-1-colliding-names.rs @@ -4,7 +4,7 @@ extern crate rocket; #[get("/")] -fn todo(todo: &str) -> &str { +fn todo(todo: String) -> String { todo } diff --git a/contrib/src/uuid.rs b/contrib/src/uuid.rs index a12232b4..6b9de682 100644 --- a/contrib/src/uuid.rs +++ b/contrib/src/uuid.rs @@ -84,7 +84,7 @@ impl<'a> FromParam<'a> for UUID { /// A value is successfully parsed if `param` is a properly formatted UUID. /// Otherwise, a `UuidParseError` is returned. #[inline(always)] - fn from_param(param: &'a str) -> Result { + fn from_param(param: &'a RawStr) -> Result { param.parse() } } @@ -141,14 +141,14 @@ mod test { #[test] fn test_from_param() { let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2"; - let uuid_wrapper = UUID::from_param(uuid_str).unwrap(); + let uuid_wrapper = UUID::from_param(uuid_str.into()).unwrap(); assert_eq!(uuid_str, uuid_wrapper.to_string()) } #[test] fn test_into_inner() { let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2"; - let uuid_wrapper = UUID::from_param(uuid_str).unwrap(); + let uuid_wrapper = UUID::from_param(uuid_str.into()).unwrap(); let real_uuid: uuid_ext::Uuid = uuid_str.parse().unwrap(); let inner_uuid: uuid_ext::Uuid = uuid_wrapper.into_inner(); assert_eq!(real_uuid, inner_uuid) @@ -157,7 +157,7 @@ mod test { #[test] fn test_partial_eq() { let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2"; - let uuid_wrapper = UUID::from_param(uuid_str).unwrap(); + let uuid_wrapper = UUID::from_param(uuid_str.into()).unwrap(); let real_uuid: uuid_ext::Uuid = uuid_str.parse().unwrap(); assert_eq!(uuid_wrapper, real_uuid) } @@ -165,7 +165,7 @@ mod test { #[test] fn test_from_param_invalid() { let uuid_str = "c1aa1e3b-9614-4895-9ebd-705255fa5bc2p"; - let uuid_result = UUID::from_param(uuid_str); + let uuid_result = UUID::from_param(uuid_str.into()); assert_eq!(uuid_result, Err(UuidParseError::InvalidLength(37))); } } diff --git a/examples/errors/src/main.rs b/examples/errors/src/main.rs index cebdc34b..edcb3981 100644 --- a/examples/errors/src/main.rs +++ b/examples/errors/src/main.rs @@ -8,7 +8,7 @@ extern crate rocket; use rocket::response::content; #[get("/hello//")] -fn hello(name: &str, age: i8) -> String { +fn hello(name: String, age: i8) -> String { format!("Hello, {} year old named {}!", age, name) } diff --git a/examples/extended_validation/src/main.rs b/examples/extended_validation/src/main.rs index 75963d59..45dfd151 100644 --- a/examples/extended_validation/src/main.rs +++ b/examples/extended_validation/src/main.rs @@ -75,7 +75,7 @@ fn login<'a>(user_form: Form<'a, UserLogin<'a>>) -> Result { } #[get("/user/")] -fn user_page(username: &str) -> String { +fn user_page(username: &RawStr) -> String { format!("This is {}'s page.", username) } diff --git a/examples/hello_person/src/main.rs b/examples/hello_person/src/main.rs index cb0c993d..a4769efe 100644 --- a/examples/hello_person/src/main.rs +++ b/examples/hello_person/src/main.rs @@ -6,12 +6,12 @@ extern crate rocket; #[cfg(test)] mod tests; #[get("/hello//")] -fn hello(name: &str, age: u8) -> String { +fn hello(name: String, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) } #[get("/hello/")] -fn hi<'r>(name: &'r str) -> &'r str { +fn hi(name: String) -> String { name } diff --git a/examples/hello_ranks/src/main.rs b/examples/hello_ranks/src/main.rs index f8d12abd..ab1d6f6a 100644 --- a/examples/hello_ranks/src/main.rs +++ b/examples/hello_ranks/src/main.rs @@ -3,15 +3,17 @@ extern crate rocket; +use rocket::http::RawStr; + #[cfg(test)] mod tests; #[get("/hello//")] -fn hello(name: &str, age: i8) -> String { +fn hello(name: String, age: i8) -> String { format!("Hello, {} year old named {}!", age, name) } #[get("/hello//", rank = 2)] -fn hi(name: &str, age: &str) -> String { +fn hi(name: String, age: &RawStr) -> String { format!("Hi {}! Your age ({}) is kind of funky.", name, age) } diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index 1e23f3f7..8720c2be 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -7,8 +7,7 @@ use std::io; use std::fs::File; use rocket::{Request, Route, Data, Catcher, Error}; -use rocket::http::Status; -use rocket::request::FromParam; +use rocket::http::{Status, RawStr}; use rocket::response::{self, Responder}; use rocket::response::status::Custom; use rocket::handler::Outcome; @@ -23,7 +22,8 @@ fn hi(_req: &Request, _: Data) -> Outcome<'static> { } fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> { - Outcome::of(req.get_param(0).unwrap_or("unnamed")) + let param = req.get_param::<&'a RawStr>(0); + Outcome::of(param.map(|r| r.as_str()).unwrap_or("unnamed")) } fn echo_url(req: &Request, _: Data) -> Outcome<'static> { @@ -31,7 +31,8 @@ fn echo_url(req: &Request, _: Data) -> Outcome<'static> { .as_str() .split_at(6) .1; - Outcome::of(String::from_param(param).unwrap()) + + Outcome::of(RawStr::from_str(param).url_decode()) } fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> { diff --git a/examples/optional_redirect/src/main.rs b/examples/optional_redirect/src/main.rs index a25fcf08..693ae1ec 100644 --- a/examples/optional_redirect/src/main.rs +++ b/examples/optional_redirect/src/main.rs @@ -6,6 +6,7 @@ extern crate rocket; mod tests; use rocket::response::Redirect; +use rocket::http::RawStr; #[get("/")] fn root() -> Redirect { @@ -13,8 +14,8 @@ fn root() -> Redirect { } #[get("/users/")] -fn user(name: &str) -> Result<&'static str, Redirect> { - match name { +fn user(name: &RawStr) -> Result<&'static str, Redirect> { + match name.as_str() { "Sergio" => Ok("Hello, Sergio!"), _ => Err(Redirect::to("/users/login")), } diff --git a/examples/optional_result/src/main.rs b/examples/optional_result/src/main.rs index 7b47f32c..8edba794 100644 --- a/examples/optional_result/src/main.rs +++ b/examples/optional_result/src/main.rs @@ -3,11 +3,12 @@ extern crate rocket; -#[cfg(test)] -mod tests; +#[cfg(test)] mod tests; + +use rocket::http::RawStr; #[get("/users/")] -fn user(name: &str) -> Option<&'static str> { +fn user(name: &RawStr) -> Option<&'static str> { if name == "Sergio" { Some("Hello, Sergio!") } else { diff --git a/examples/pastebin/src/paste_id.rs b/examples/pastebin/src/paste_id.rs index fee1aec3..2586b302 100644 --- a/examples/pastebin/src/paste_id.rs +++ b/examples/pastebin/src/paste_id.rs @@ -2,6 +2,7 @@ use std::fmt; use std::borrow::Cow; use rocket::request::FromParam; +use rocket::http::RawStr; use rand::{self, Rng}; /// Table to retrieve base62 values from. @@ -44,9 +45,9 @@ fn valid_id(id: &str) -> bool { /// Returns an instance of `PasteID` if the path segment is a valid ID. /// Otherwise returns the invalid ID as the `Err` value. impl<'a> FromParam<'a> for PasteID<'a> { - type Error = &'a str; + type Error = &'a RawStr; - fn from_param(param: &'a str) -> Result, &'a str> { + fn from_param(param: &'a RawStr) -> Result, &'a RawStr> { match valid_id(param) { true => Ok(PasteID(Cow::Borrowed(param))), false => Err(param) diff --git a/lib/src/request/param.rs b/lib/src/request/param.rs index bf3f9807..7602e277 100644 --- a/lib/src/request/param.rs +++ b/lib/src/request/param.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use std::fmt::Debug; use http::uri::{URI, Segments, SegmentError}; +use http::RawStr; /// Trait to convert a dynamic path segment string to a concrete value. /// @@ -51,15 +52,16 @@ use http::uri::{URI, Segments, SegmentError}; /// /// For instance, imagine you've asked for an `` as a `usize`. To determine /// when the `` was not a valid `usize` and retrieve the string that failed -/// to parse, you can use a `Result` type for the `` parameter -/// as follows: +/// to parse, you can use a `Result` type for the `` +/// parameter as follows: /// /// ```rust /// # #![feature(plugin)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; +/// # use rocket::http::RawStr; /// #[get("/")] -/// fn hello(id: Result) -> String { +/// fn hello(id: Result) -> String { /// match id { /// Ok(id_num) => format!("usize: {}", id_num), /// Err(string) => format!("Not a usize: {}", string) @@ -80,7 +82,7 @@ use http::uri::{URI, Segments, SegmentError}; /// type returns successfully. Otherwise, the raw path segment is returned /// in the `Err` value. /// -/// * **str** +/// * **&RawStr** /// /// _This implementation always returns successfully._ /// @@ -107,14 +109,6 @@ use http::uri::{URI, Segments, SegmentError}; /// The path segment is parsed by `T`'s `FromParam` implementation. The /// returned `Result` value is returned. /// -/// # `str` vs. `String` -/// -/// Paths are URL encoded. As a result, the `str` `FromParam` implementation -/// returns the raw, URL encoded version of the path segment string. On the -/// other hand, `String` decodes the path parameter, but requires an allocation -/// to do so. This tradeoff is similiar to that of form values, and you should -/// use whichever makes sense for your application. -/// /// # Example /// /// Say you want to parse a segment of the form: @@ -138,13 +132,14 @@ use http::uri::{URI, Segments, SegmentError}; /// /// ```rust /// use rocket::request::FromParam; +/// use rocket::http::RawStr; /// # #[allow(dead_code)] /// # struct MyParam<'r> { key: &'r str, value: usize } /// /// impl<'r> FromParam<'r> for MyParam<'r> { -/// type Error = &'r str; +/// type Error = &'r RawStr; /// -/// fn from_param(param: &'r str) -> Result, &'r str> { +/// fn from_param(param: &'r RawStr) -> Result { /// let (key, val_str) = match param.find(':') { /// Some(i) if i > 0 => (¶m[..i], ¶m[(i + 1)..]), /// _ => return Err(param) @@ -172,11 +167,12 @@ use http::uri::{URI, Segments, SegmentError}; /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # use rocket::request::FromParam; +/// # use rocket::http::RawStr; /// # #[allow(dead_code)] /// # struct MyParam<'r> { key: &'r str, value: usize } /// # impl<'r> FromParam<'r> for MyParam<'r> { -/// # type Error = &'r str; -/// # fn from_param(param: &'r str) -> Result, &'r str> { +/// # type Error = &'r RawStr; +/// # fn from_param(param: &'r RawStr) -> Result { /// # Err(param) /// # } /// # } @@ -197,29 +193,35 @@ pub trait FromParam<'a>: Sized { /// Parses an instance of `Self` from a dynamic path parameter string or /// returns an `Error` if one cannot be parsed. - fn from_param(param: &'a str) -> Result; + fn from_param(param: &'a RawStr) -> Result; } -impl<'a> FromParam<'a> for &'a str { +impl<'a> FromParam<'a> for &'a RawStr { type Error = (); - fn from_param(param: &'a str) -> Result<&'a str, Self::Error> { + + #[inline(always)] + fn from_param(param: &'a RawStr) -> Result<&'a RawStr, Self::Error> { Ok(param) } } impl<'a> FromParam<'a> for String { - type Error = &'a str; - fn from_param(p: &'a str) -> Result { - URI::percent_decode(p.as_bytes()).map_err(|_| p).map(|s| s.into_owned()) + type Error = &'a RawStr; + + #[inline(always)] + fn from_param(param: &'a RawStr) -> Result { + param.percent_decode().map(|cow| cow.into_owned()).map_err(|_| param) } } macro_rules! impl_with_fromstr { ($($T:ident),+) => ($( impl<'a> FromParam<'a> for $T { - type Error = &'a str; - fn from_param(param: &'a str) -> Result { - $T::from_str(param).map_err(|_| param) + type Error = &'a RawStr; + + #[inline(always)] + fn from_param(param: &'a RawStr) -> Result { + $T::from_str(param.as_str()).map_err(|_| param) } } )+) @@ -230,22 +232,26 @@ impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64, SocketAddr); impl<'a, T: FromParam<'a>> FromParam<'a> for Result { - type Error = (); - fn from_param(p: &'a str) -> Result { - Ok(match T::from_param(p) { - Ok(val) => Ok(val), - Err(e) => Err(e), - }) + type Error = !; + + #[inline] + fn from_param(param: &'a RawStr) -> Result { + match T::from_param(param) { + Ok(val) => Ok(Ok(val)), + Err(e) => Ok(Err(e)), + } } } impl<'a, T: FromParam<'a>> FromParam<'a> for Option { - type Error = (); - fn from_param(p: &'a str) -> Result { - Ok(match T::from_param(p) { - Ok(val) => Some(val), - Err(_) => None - }) + type Error = !; + + #[inline] + fn from_param(param: &'a RawStr) -> Result { + match T::from_param(param) { + Ok(val) => Ok(Some(val)), + Err(_) => Ok(None) + } } } @@ -277,9 +283,10 @@ pub trait FromSegments<'a>: Sized { } impl<'a> FromSegments<'a> for Segments<'a> { - type Error = (); + type Error = !; - fn from_segments(segments: Segments<'a>) -> Result, ()> { + #[inline(always)] + fn from_segments(segments: Segments<'a>) -> Result, Self::Error> { Ok(segments) } } @@ -335,21 +342,25 @@ impl<'a> FromSegments<'a> for PathBuf { } impl<'a, T: FromSegments<'a>> FromSegments<'a> for Result { - type Error = (); - fn from_segments(segments: Segments<'a>) -> Result, ()> { - Ok(match T::from_segments(segments) { - Ok(val) => Ok(val), - Err(e) => Err(e), - }) + type Error = !; + + #[inline] + fn from_segments(segments: Segments<'a>) -> Result, !> { + match T::from_segments(segments) { + Ok(val) => Ok(Ok(val)), + Err(e) => Ok(Err(e)), + } } } impl<'a, T: FromSegments<'a>> FromSegments<'a> for Option { - type Error = (); - fn from_segments(segments: Segments<'a>) -> Result, ()> { - Ok(match T::from_segments(segments) { - Ok(val) => Some(val), - Err(_) => None - }) + type Error = !; + + #[inline] + fn from_segments(segments: Segments<'a>) -> Result, !> { + match T::from_segments(segments) { + Ok(val) => Ok(Some(val)), + Err(_) => Ok(None) + } } } diff --git a/lib/src/request/request.rs b/lib/src/request/request.rs index 6d607463..07ce0101 100644 --- a/lib/src/request/request.rs +++ b/lib/src/request/request.rs @@ -14,7 +14,7 @@ use super::{FromParam, FromSegments}; use router::Route; use http::uri::{URI, Segments}; use http::{Method, Header, HeaderMap, Cookies, Session, CookieJar, Key}; -use http::{ContentType, Accept, MediaType}; +use http::{RawStr, ContentType, Accept, MediaType}; use http::parse::media_type; use http::hyper; @@ -362,7 +362,7 @@ impl<'r> Request<'r> { /// /// # Example /// - /// Retrieve parameter `0`, which is expected to be an `&str`, in a manual + /// Retrieve parameter `0`, which is expected to be a `String`, in a manual /// route: /// /// ```rust @@ -371,7 +371,7 @@ impl<'r> Request<'r> { /// /// # #[allow(dead_code)] /// fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> { - /// Outcome::of(req.get_param(0).unwrap_or("unnamed")) + /// Outcome::of(req.get_param::(0).unwrap_or("unnamed".into())) /// } /// ``` pub fn get_param<'a, T: FromParam<'a>>(&'a self, n: usize) -> Result { @@ -391,7 +391,7 @@ impl<'r> Request<'r> { /// Get the `n`th path parameter as a string, if it exists. This is used by /// codegen. #[doc(hidden)] - pub fn get_param_str(&self, n: usize) -> Option<&str> { + pub fn get_param_str(&self, n: usize) -> Option<&RawStr> { let params = self.extra.params.borrow(); if n >= params.len() { debug!("{} is >= param count {}", n, params.len()); @@ -405,7 +405,7 @@ impl<'r> Request<'r> { return None; } - Some(&path[i..j]) + Some(path[i..j].into()) } /// Retrieves and parses into `T` all of the path segments in the request diff --git a/lib/src/response/flash.rs b/lib/src/response/flash.rs index ceb629e2..1fe1cf7f 100644 --- a/lib/src/response/flash.rs +++ b/lib/src/response/flash.rs @@ -53,9 +53,10 @@ const FLASH_COOKIE_NAME: &'static str = "_flash"; /// # /// use rocket::response::{Flash, Redirect}; /// use rocket::request::FlashMessage; +/// use rocket::http::RawStr; /// /// #[post("/login/")] -/// fn login(name: &str) -> Result<&'static str, Flash> { +/// fn login(name: &RawStr) -> Result<&'static str, Flash> { /// if name == "special_user" { /// Ok("Hello, special user!") /// } else {