Use the `RawStr` type for raw parameter strings.

This is a breaking change.

The `&str` type no longer implements `FromParam`. The `&RawStr` type
should be used in its place.
This commit is contained in:
Sergio Benitez 2017-03-31 00:18:58 -07:00
parent cff9901940
commit f5ec470a7d
15 changed files with 100 additions and 82 deletions

View File

@ -13,7 +13,7 @@ struct User<'a> {
}
#[post("/<name>?<query>", format = "application/json", data = "<user>", rank = 2)]
fn get<'r>(name: &str,
fn get<'r>(name: &RawStr,
query: User<'r>,
user: Form<'r, User<'r>>,
cookies: Cookies)

View File

@ -4,7 +4,7 @@
extern crate rocket;
#[get("/test/<one>/<two>/<three>")]
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];

View File

@ -4,7 +4,7 @@
extern crate rocket;
#[get("/<todo>")]
fn todo(todo: &str) -> &str {
fn todo(todo: String) -> String {
todo
}

View File

@ -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<UUID, Self::Error> {
fn from_param(param: &'a RawStr) -> Result<UUID, Self::Error> {
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)));
}
}

View File

@ -8,7 +8,7 @@ extern crate rocket;
use rocket::response::content;
#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: i8) -> String {
fn hello(name: String, age: i8) -> String {
format!("Hello, {} year old named {}!", age, name)
}

View File

@ -75,7 +75,7 @@ fn login<'a>(user_form: Form<'a, UserLogin<'a>>) -> Result<Redirect, String> {
}
#[get("/user/<username>")]
fn user_page(username: &str) -> String {
fn user_page(username: &RawStr) -> String {
format!("This is {}'s page.", username)
}

View File

@ -6,12 +6,12 @@ extern crate rocket;
#[cfg(test)] mod tests;
#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
fn hello(name: String, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
#[get("/hello/<name>")]
fn hi<'r>(name: &'r str) -> &'r str {
fn hi(name: String) -> String {
name
}

View File

@ -3,15 +3,17 @@
extern crate rocket;
use rocket::http::RawStr;
#[cfg(test)] mod tests;
#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: i8) -> String {
fn hello(name: String, age: i8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
#[get("/hello/<name>/<age>", 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)
}

View File

@ -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> {

View File

@ -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/<name>")]
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")),
}

View File

@ -3,11 +3,12 @@
extern crate rocket;
#[cfg(test)]
mod tests;
#[cfg(test)] mod tests;
use rocket::http::RawStr;
#[get("/users/<name>")]
fn user(name: &str) -> Option<&'static str> {
fn user(name: &RawStr) -> Option<&'static str> {
if name == "Sergio" {
Some("Hello, Sergio!")
} else {

View File

@ -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<PasteID<'a>, &'a str> {
fn from_param(param: &'a RawStr) -> Result<PasteID<'a>, &'a RawStr> {
match valid_id(param) {
true => Ok(PasteID(Cow::Borrowed(param))),
false => Err(param)

View File

@ -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 `<id>` as a `usize`. To determine
/// when the `<id>` was not a valid `usize` and retrieve the string that failed
/// to parse, you can use a `Result<usize, &str>` type for the `<id>` parameter
/// as follows:
/// to parse, you can use a `Result<usize, &RawStr>` type for the `<id>`
/// parameter as follows:
///
/// ```rust
/// # #![feature(plugin)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// # use rocket::http::RawStr;
/// #[get("/<id>")]
/// fn hello(id: Result<usize, &str>) -> String {
/// fn hello(id: Result<usize, &RawStr>) -> 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<MyParam<'r>, &'r str> {
/// fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
/// let (key, val_str) = match param.find(':') {
/// Some(i) if i > 0 => (&param[..i], &param[(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<MyParam<'r>, &'r str> {
/// # type Error = &'r RawStr;
/// # fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
/// # 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<Self, Self::Error>;
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error>;
}
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<String, Self::Error> {
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<String, Self::Error> {
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<Self, Self::Error> {
$T::from_str(param).map_err(|_| param)
type Error = &'a RawStr;
#[inline(always)]
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
$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<T, T::Error> {
type Error = ();
fn from_param(p: &'a str) -> Result<Self, Self::Error> {
Ok(match T::from_param(p) {
Ok(val) => Ok(val),
Err(e) => Err(e),
})
type Error = !;
#[inline]
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
match T::from_param(param) {
Ok(val) => Ok(Ok(val)),
Err(e) => Ok(Err(e)),
}
}
}
impl<'a, T: FromParam<'a>> FromParam<'a> for Option<T> {
type Error = ();
fn from_param(p: &'a str) -> Result<Self, Self::Error> {
Ok(match T::from_param(p) {
Ok(val) => Some(val),
Err(_) => None
})
type Error = !;
#[inline]
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
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<Segments<'a>, ()> {
#[inline(always)]
fn from_segments(segments: Segments<'a>) -> Result<Segments<'a>, Self::Error> {
Ok(segments)
}
}
@ -335,21 +342,25 @@ impl<'a> FromSegments<'a> for PathBuf {
}
impl<'a, T: FromSegments<'a>> FromSegments<'a> for Result<T, T::Error> {
type Error = ();
fn from_segments(segments: Segments<'a>) -> Result<Result<T, T::Error>, ()> {
Ok(match T::from_segments(segments) {
Ok(val) => Ok(val),
Err(e) => Err(e),
})
type Error = !;
#[inline]
fn from_segments(segments: Segments<'a>) -> Result<Result<T, T::Error>, !> {
match T::from_segments(segments) {
Ok(val) => Ok(Ok(val)),
Err(e) => Ok(Err(e)),
}
}
}
impl<'a, T: FromSegments<'a>> FromSegments<'a> for Option<T> {
type Error = ();
fn from_segments(segments: Segments<'a>) -> Result<Option<T>, ()> {
Ok(match T::from_segments(segments) {
Ok(val) => Some(val),
Err(_) => None
})
type Error = !;
#[inline]
fn from_segments(segments: Segments<'a>) -> Result<Option<T>, !> {
match T::from_segments(segments) {
Ok(val) => Ok(Some(val)),
Err(_) => Ok(None)
}
}
}

View File

@ -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::<String>(0).unwrap_or("unnamed".into()))
/// }
/// ```
pub fn get_param<'a, T: FromParam<'a>>(&'a self, n: usize) -> Result<T, Error> {
@ -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

View File

@ -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/<name>")]
/// fn login(name: &str) -> Result<&'static str, Flash<Redirect>> {
/// fn login(name: &RawStr) -> Result<&'static str, Flash<Redirect>> {
/// if name == "special_user" {
/// Ok("Hello, special user!")
/// } else {