mirror of https://github.com/rwf2/Rocket.git
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.
This commit is contained in:
parent
f57d984e2e
commit
0c44e44641
|
@ -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 })
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Self, Self::Error> {
|
||||
let variant = match v {
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
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<T> {
|
||||
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<ManualMethod> = 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<UnpresentCheckboxTwo> = parse("something=hello");
|
||||
assert_eq!(manual, Some(UnpresentCheckboxTwo {
|
||||
checkbox: false,
|
||||
something: "hello"
|
||||
something: "hello".into()
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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<UUID, &'v str> {
|
||||
#[inline(always)]
|
||||
fn from_form_value(form_value: &'v RawStr) -> Result<UUID, &'v RawStr> {
|
||||
form_value.parse().map_err(|_| form_value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<StrongPassword<'r>, &'static str>,
|
||||
age: Result<AdultAge, &'static str>,
|
||||
}
|
||||
|
@ -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<Self, Self::Error> {
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
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<Self, Self::Error> {
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
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."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<Self, Self::Error> {
|
||||
let variant = match v {
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
let variant = match v.as_str() {
|
||||
"a" => FormOption::A,
|
||||
"b" => FormOption::B,
|
||||
"c" => FormOption::C,
|
||||
|
|
|
@ -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<usize, &'r str>,
|
||||
age: Result<usize, &'r RawStr>,
|
||||
}
|
||||
|
||||
#[post("/login", data = "<user_form>")]
|
||||
|
@ -36,9 +37,8 @@ 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: String) -> String {
|
||||
format!("This is {}'s page.", username)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<u8>
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#![feature(type_ascription)]
|
||||
#![feature(lookup_host)]
|
||||
#![feature(plugin)]
|
||||
#![feature(never_type)]
|
||||
|
||||
#![plugin(pear_codegen)]
|
||||
|
||||
|
|
|
@ -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<Self::Item> {
|
||||
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)));
|
||||
}
|
||||
|
|
|
@ -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<u16, &'r str>
|
||||
/// age: Result<u16, &'r RawStr>
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The `Err` value in this case is `&str` since `u16::from_form_value` returns
|
||||
/// a `Result<u16, &str>`.
|
||||
/// The `Err` value in this case is `&RawStr` since `u16::from_form_value`
|
||||
/// returns a `Result<u16, &RawStr>`.
|
||||
///
|
||||
/// # 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<AdultAge, &'v str> {
|
||||
/// match usize::from_form_value(form_value) {
|
||||
/// fn from_form_value(form_value: &'v RawStr) -> Result<AdultAge, &'v RawStr> {
|
||||
/// match form_value.parse::<usize>() {
|
||||
/// 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<Self, Self::Error>;
|
||||
fn from_form_value(form_value: &'v RawStr) -> Result<Self, Self::Error>;
|
||||
|
||||
/// 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<Self> {
|
||||
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<Self, Self::Error> {
|
||||
#[inline(always)]
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
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<Self, Self::Error> {
|
||||
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<Self, Self::Error> {
|
||||
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<Self, Self::Error> {
|
||||
match v {
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
match v.as_str() {
|
||||
"on" | "true" => Ok(true),
|
||||
"off" | "false" => Ok(false),
|
||||
_ => Err(v),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default() -> Option<bool> {
|
||||
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<Self, Self::Error> {
|
||||
$T::from_str(v).map_err(|_| v)
|
||||
type Error = &'v RawStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
$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<T> {
|
||||
type Error = Error;
|
||||
type Error = !;
|
||||
|
||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||
#[inline(always)]
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
match T::from_form_value(v) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
Err(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default() -> Option<Option<T>> {
|
||||
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<T, T::Error> {
|
||||
type Error = Error;
|
||||
type Error = !;
|
||||
|
||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||
#[inline(always)]
|
||||
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||
match T::from_form_value(v) {
|
||||
ok@Ok(_) => Ok(ok),
|
||||
e@Err(_) => Ok(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = "<user_input>")]
|
||||
/// 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue