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,
|
is_unsafe: false,
|
||||||
supports_unions: false,
|
supports_unions: false,
|
||||||
span: span,
|
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: ty::Path {
|
||||||
path: vec!["rocket", "request", "FromForm"],
|
path: vec!["rocket", "request", "FromForm"],
|
||||||
lifetime: lifetime_var,
|
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();
|
let id_str = ident_string.as_str();
|
||||||
arms.push(quote_tokens!(cx,
|
arms.push(quote_tokens!(cx,
|
||||||
$id_str => {
|
$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),
|
Ok(v) => Some(v),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(" => Error parsing form val '{}': {:?}",
|
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.
|
// and use the $arms generated above.
|
||||||
stmts.push(quote_stmt!(cx,
|
stmts.push(quote_stmt!(cx,
|
||||||
for (k, v) in $arg {
|
for (k, v) in $arg {
|
||||||
match k {
|
match k.as_str() {
|
||||||
$arms
|
$arms
|
||||||
field if field == "_method" => {
|
"_method" => {
|
||||||
/* This is a Rocket-specific field. If the user hasn't asked
|
/* This is a Rocket-specific field. If the user hasn't asked
|
||||||
* for it, just let it go by without error. This should stay
|
* for it, just let it go by without error. This should stay
|
||||||
* in sync with Rocket::preprocess. */
|
* 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.
|
// that each parameter actually is Some() or has a default value.
|
||||||
let mut failure_conditions = vec![];
|
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() {
|
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,
|
failure_conditions.push(quote_tokens!(cx,
|
||||||
if $ident.is_none() &&
|
if $ident.is_none() &&
|
||||||
<$ty as ::rocket::request::FromFormValue>::default().is_none() {
|
<$ty as ::rocket::request::FromFormValue>::default().is_none() {
|
||||||
println!(" => '{}' did not parse.", stringify!($ident));
|
println!(" => '{}' did not parse.", stringify!($ident));
|
||||||
true
|
$return_err_stmt;
|
||||||
} else { false }
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,9 +244,7 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
||||||
// the structure.
|
// the structure.
|
||||||
let self_ident = substr.type_ident;
|
let self_ident = substr.type_ident;
|
||||||
let final_block = quote_block!(cx, {
|
let final_block = quote_block!(cx, {
|
||||||
if $failure_conditions {
|
$failure_conditions
|
||||||
$return_err_stmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok($self_ident { $result_fields })
|
Ok($self_ident { $result_fields })
|
||||||
});
|
});
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl RouteGenerateExt for RouteParams {
|
||||||
Err(_) => return ::rocket::Outcome::Forward(_data)
|
Err(_) => return ::rocket::Outcome::Forward(_data)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !items.exhausted() {
|
if !items.exhaust() {
|
||||||
println!(" => The query string {:?} is malformed.", $form_string);
|
println!(" => The query string {:?} is malformed.", $form_string);
|
||||||
return ::rocket::Outcome::Failure(::rocket::http::Status::BadRequest);
|
return ::rocket::Outcome::Failure(::rocket::http::Status::BadRequest);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use rocket::http::Cookies;
|
use rocket::http::{Cookies, RawStr};
|
||||||
use rocket::request::Form;
|
use rocket::request::Form;
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct User<'a> {
|
struct User<'a> {
|
||||||
name: &'a str,
|
name: &'a RawStr,
|
||||||
nickname: String,
|
nickname: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use rocket::request::{FromForm, FromFormValue, FormItems};
|
use rocket::request::{FromForm, FromFormValue, FormItems};
|
||||||
|
use rocket::http::RawStr;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, FromForm)]
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
struct TodoTask {
|
struct TodoTask {
|
||||||
|
@ -20,8 +21,8 @@ enum FormOption {
|
||||||
impl<'v> FromFormValue<'v> for FormOption {
|
impl<'v> FromFormValue<'v> for FormOption {
|
||||||
type Error = &'v str;
|
type Error = &'v str;
|
||||||
|
|
||||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||||
let variant = match v {
|
let variant = match v.as_str() {
|
||||||
"a" => FormOption::A,
|
"a" => FormOption::A,
|
||||||
"b" => FormOption::B,
|
"b" => FormOption::B,
|
||||||
"c" => FormOption::C,
|
"c" => FormOption::C,
|
||||||
|
@ -37,19 +38,19 @@ struct FormInput<'r> {
|
||||||
checkbox: bool,
|
checkbox: bool,
|
||||||
number: usize,
|
number: usize,
|
||||||
radio: FormOption,
|
radio: FormOption,
|
||||||
password: &'r str,
|
password: &'r RawStr,
|
||||||
textarea: String,
|
textarea: String,
|
||||||
select: FormOption,
|
select: FormOption,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, FromForm)]
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
struct DefaultInput<'r> {
|
struct DefaultInput<'r> {
|
||||||
arg: Option<&'r str>,
|
arg: Option<&'r RawStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, FromForm)]
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
struct ManualMethod<'r> {
|
struct ManualMethod<'r> {
|
||||||
_method: Option<&'r str>,
|
_method: Option<&'r RawStr>,
|
||||||
done: bool
|
done: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,13 +62,13 @@ struct UnpresentCheckbox {
|
||||||
#[derive(Debug, PartialEq, FromForm)]
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
struct UnpresentCheckboxTwo<'r> {
|
struct UnpresentCheckboxTwo<'r> {
|
||||||
checkbox: bool,
|
checkbox: bool,
|
||||||
something: &'r str
|
something: &'r RawStr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse<'f, T: FromForm<'f>>(string: &'f str) -> Option<T> {
|
fn parse<'f, T: FromForm<'f>>(string: &'f str) -> Option<T> {
|
||||||
let mut items = FormItems::from(string);
|
let mut items = FormItems::from(string);
|
||||||
let result = T::from_form_items(items.by_ref());
|
let result = T::from_form_items(items.by_ref());
|
||||||
if !items.exhausted() {
|
if !items.exhaust() {
|
||||||
panic!("Invalid form input.");
|
panic!("Invalid form input.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ fn main() {
|
||||||
checkbox: false,
|
checkbox: false,
|
||||||
number: 10,
|
number: 10,
|
||||||
radio: FormOption::C,
|
radio: FormOption::C,
|
||||||
password: "testing",
|
password: "testing".into(),
|
||||||
textarea: "".to_string(),
|
textarea: "".to_string(),
|
||||||
select: FormOption::A,
|
select: FormOption::A,
|
||||||
}));
|
}));
|
||||||
|
@ -117,7 +118,7 @@ fn main() {
|
||||||
// Ensure _method can be captured if desired.
|
// Ensure _method can be captured if desired.
|
||||||
let manual: Option<ManualMethod> = parse("_method=put&done=true");
|
let manual: Option<ManualMethod> = parse("_method=put&done=true");
|
||||||
assert_eq!(manual, Some(ManualMethod {
|
assert_eq!(manual, Some(ManualMethod {
|
||||||
_method: Some("put"),
|
_method: Some("put".into()),
|
||||||
done: true
|
done: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -138,6 +139,6 @@ fn main() {
|
||||||
let manual: Option<UnpresentCheckboxTwo> = parse("something=hello");
|
let manual: Option<UnpresentCheckboxTwo> = parse("something=hello");
|
||||||
assert_eq!(manual, Some(UnpresentCheckboxTwo {
|
assert_eq!(manual, Some(UnpresentCheckboxTwo {
|
||||||
checkbox: false,
|
checkbox: false,
|
||||||
something: "hello"
|
something: "hello".into()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::str::FromStr;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use rocket::request::{FromParam, FromFormValue};
|
use rocket::request::{FromParam, FromFormValue};
|
||||||
|
use rocket::http::RawStr;
|
||||||
|
|
||||||
pub use self::uuid_ext::ParseError as UuidParseError;
|
pub use self::uuid_ext::ParseError as UuidParseError;
|
||||||
|
|
||||||
|
@ -89,11 +90,12 @@ impl<'a> FromParam<'a> for UUID {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'v> FromFormValue<'v> 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
|
/// A value is successfully parsed if `form_value` is a properly formatted
|
||||||
/// UUID. Otherwise, the raw form value is returned.
|
/// 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)
|
form_value.parse().map_err(|_| form_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
mod files;
|
mod files;
|
||||||
#[cfg(test)]
|
#[cfg(test)] mod tests;
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket::request::{Form, FromFormValue};
|
use rocket::request::{Form, FromFormValue};
|
||||||
|
use rocket::http::RawStr;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct StrongPassword<'r>(&'r str);
|
struct StrongPassword<'r>(&'r str);
|
||||||
|
@ -18,7 +18,7 @@ struct AdultAge(isize);
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct UserLogin<'r> {
|
struct UserLogin<'r> {
|
||||||
username: &'r str,
|
username: &'r RawStr,
|
||||||
password: Result<StrongPassword<'r>, &'static str>,
|
password: Result<StrongPassword<'r>, &'static str>,
|
||||||
age: Result<AdultAge, &'static str>,
|
age: Result<AdultAge, &'static str>,
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,11 @@ struct UserLogin<'r> {
|
||||||
impl<'v> FromFormValue<'v> for StrongPassword<'v> {
|
impl<'v> FromFormValue<'v> for StrongPassword<'v> {
|
||||||
type Error = &'static str;
|
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 {
|
if v.len() < 8 {
|
||||||
Err("Too short!")
|
Err("too short!")
|
||||||
} else {
|
} 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 {
|
impl<'v> FromFormValue<'v> for AdultAge {
|
||||||
type Error = &'static str;
|
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) {
|
let age = match isize::from_form_value(v) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return Err("Age value is not a number."),
|
Err(_) => return Err("value is not a number."),
|
||||||
};
|
};
|
||||||
|
|
||||||
match age > 20 {
|
match age > 20 {
|
||||||
true => Ok(AdultAge(age)),
|
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]
|
#[test]
|
||||||
fn test_invalid_password() {
|
fn test_invalid_password() {
|
||||||
test_login("Sergio", "password1", "30", Status::Ok, "Wrong 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]
|
#[test]
|
||||||
fn test_invalid_age() {
|
fn test_invalid_age() {
|
||||||
test_login("Sergio", "password", "20", Status::Ok, "Must be at least 21.");
|
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", "-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", "hi", Status::Ok, "value is not a number");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_bad_form(form_str: &str, status: Status) {
|
fn check_bad_form(form_str: &str, status: Status) {
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use rocket::request::{Form, FromFormValue};
|
use rocket::request::{Form, FromFormValue};
|
||||||
use rocket::response::NamedFile;
|
use rocket::response::NamedFile;
|
||||||
use std::io;
|
use rocket::http::RawStr;
|
||||||
|
|
||||||
// TODO: Make deriving `FromForm` for this enum possible.
|
// TODO: Make deriving `FromForm` for this enum possible.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -14,10 +15,10 @@ enum FormOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'v> FromFormValue<'v> for 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> {
|
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||||
let variant = match v {
|
let variant = match v.as_str() {
|
||||||
"a" => FormOption::A,
|
"a" => FormOption::A,
|
||||||
"b" => FormOption::B,
|
"b" => FormOption::B,
|
||||||
"c" => FormOption::C,
|
"c" => FormOption::C,
|
||||||
|
|
|
@ -8,12 +8,13 @@ mod files;
|
||||||
|
|
||||||
use rocket::request::Form;
|
use rocket::request::Form;
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
|
use rocket::http::RawStr;
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct UserLogin<'r> {
|
struct UserLogin<'r> {
|
||||||
username: &'r str,
|
username: &'r RawStr,
|
||||||
password: String,
|
password: String,
|
||||||
age: Result<usize, &'r str>,
|
age: Result<usize, &'r RawStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<user_form>")]
|
#[post("/login", data = "<user_form>")]
|
||||||
|
@ -36,9 +37,8 @@ fn login<'a>(user_form: Form<'a, UserLogin<'a>>) -> Result<Redirect, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[get("/user/<username>")]
|
#[get("/user/<username>")]
|
||||||
fn user_page(username: &str) -> String {
|
fn user_page(username: String) -> String {
|
||||||
format!("This is {}'s page.", username)
|
format!("This is {}'s page.", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ extern crate rocket;
|
||||||
#[cfg(test)] mod tests;
|
#[cfg(test)] mod tests;
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct Person<'r> {
|
struct Person {
|
||||||
name: &'r str,
|
name: String,
|
||||||
age: Option<u8>
|
age: Option<u8>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#![feature(type_ascription)]
|
#![feature(type_ascription)]
|
||||||
#![feature(lookup_host)]
|
#![feature(lookup_host)]
|
||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
|
#![feature(never_type)]
|
||||||
|
|
||||||
#![plugin(pear_codegen)]
|
#![plugin(pear_codegen)]
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
use memchr::memchr2;
|
use memchr::memchr2;
|
||||||
|
|
||||||
|
use http::RawStr;
|
||||||
|
|
||||||
/// Iterator over the key/value pairs of a given HTTP form string.
|
/// 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
|
/// **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
|
/// ```rust
|
||||||
/// use rocket::request::{FormItems, FromFormValue};
|
/// use rocket::request::{FormItems, FromFormValue};
|
||||||
///
|
///
|
||||||
/// let form_string = "greeting=Hello%2C+Mark%21&username=jake%2Fother";
|
/// let form_string = "greeting=Hello%2C+Mark%21&username=jake%2Fother";
|
||||||
/// for (key, value) in FormItems::from(form_string) {
|
/// for (key, value) in FormItems::from(form_string) {
|
||||||
/// let decoded_value = String::from_form_value(value);
|
/// let decoded_value = value.url_decode();
|
||||||
/// match key {
|
/// match key.as_str() {
|
||||||
/// "greeting" => assert_eq!(decoded_value, Ok("Hello, Mark!".into())),
|
/// "greeting" => assert_eq!(decoded_value, Ok("Hello, Mark!".into())),
|
||||||
/// "username" => assert_eq!(decoded_value, Ok("jake/other".into())),
|
/// "username" => assert_eq!(decoded_value, Ok("jake/other".into())),
|
||||||
/// _ => unreachable!()
|
/// _ => unreachable!()
|
||||||
|
@ -26,7 +29,7 @@ use memchr::memchr2;
|
||||||
/// for completion via the [completed](#method.completed) method, which returns
|
/// for completion via the [completed](#method.completed) method, which returns
|
||||||
/// `true` if the iterator parsed the entire string that was passed to it. The
|
/// `true` if the iterator parsed the entire string that was passed to it. The
|
||||||
/// iterator can also attempt to parse any remaining contents via
|
/// 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.
|
/// succeeded.
|
||||||
///
|
///
|
||||||
/// This iterator guarantees that all valid form strings are parsed to
|
/// 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 form_string = "greeting=hello&username=jake";
|
||||||
/// let mut items = FormItems::from(form_string);
|
/// 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_eq!(items.next(), None);
|
||||||
/// assert!(items.completed());
|
/// assert!(items.completed());
|
||||||
/// ```
|
/// ```
|
||||||
pub struct FormItems<'f> {
|
pub struct FormItems<'f> {
|
||||||
string: &'f str,
|
string: &'f RawStr,
|
||||||
next_index: usize
|
next_index: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +127,7 @@ impl<'f> FormItems<'f> {
|
||||||
///
|
///
|
||||||
/// assert!(items.next().is_some());
|
/// assert!(items.next().is_some());
|
||||||
/// assert_eq!(items.completed(), false);
|
/// assert_eq!(items.completed(), false);
|
||||||
/// assert_eq!(items.exhausted(), true);
|
/// assert_eq!(items.exhaust(), true);
|
||||||
/// assert_eq!(items.completed(), true);
|
/// assert_eq!(items.completed(), true);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -130,10 +140,11 @@ impl<'f> FormItems<'f> {
|
||||||
///
|
///
|
||||||
/// assert!(items.next().is_some());
|
/// assert!(items.next().is_some());
|
||||||
/// assert_eq!(items.completed(), false);
|
/// assert_eq!(items.completed(), false);
|
||||||
/// assert_eq!(items.exhausted(), false);
|
/// assert_eq!(items.exhaust(), false);
|
||||||
/// assert_eq!(items.completed(), false);
|
/// assert_eq!(items.completed(), false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn exhausted(&mut self) -> bool {
|
#[inline]
|
||||||
|
pub fn exhaust(&mut self) -> bool {
|
||||||
while let Some(_) = self.next() { }
|
while let Some(_) = self.next() { }
|
||||||
self.completed()
|
self.completed()
|
||||||
}
|
}
|
||||||
|
@ -167,15 +178,16 @@ impl<'f> FormItems<'f> {
|
||||||
/// assert_eq!(items.inner_str(), form_string);
|
/// assert_eq!(items.inner_str(), form_string);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inner_str(&self) -> &'f str {
|
pub fn inner_str(&self) -> &'f RawStr {
|
||||||
self.string
|
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
|
/// Returns an iterator over the key/value pairs in the
|
||||||
/// `x-www-form-urlencoded` form `string`.
|
/// `x-www-form-urlencoded` form `string`.
|
||||||
fn from(string: &'f str) -> FormItems<'f> {
|
#[inline(always)]
|
||||||
|
fn from(string: &'f RawStr) -> FormItems<'f> {
|
||||||
FormItems {
|
FormItems {
|
||||||
string: string,
|
string: string,
|
||||||
next_index: 0
|
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> {
|
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> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let s = &self.string[self.next_index..];
|
let s = &self.string[self.next_index..];
|
||||||
|
@ -204,7 +228,7 @@ impl<'f> Iterator for FormItems<'f> {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.next_index += key.len() + 1 + consumed;
|
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_key, actual_key) = (expected[i].0, results[i].0);
|
||||||
let (expected_val, actual_val) = (expected[i].1, results[i].1);
|
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 {}",
|
"key [{}] mismatch: expected {}, got {}",
|
||||||
i, expected_key, actual_key);
|
i, expected_key, actual_key);
|
||||||
|
|
||||||
assert!(expected_val == actual_val,
|
assert!(actual_val == expected_val,
|
||||||
"val [{}] mismatch: expected {}, got {}",
|
"val [{}] mismatch: expected {}, got {}",
|
||||||
i, expected_val, actual_val);
|
i, expected_val, actual_val);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert!(!items.exhausted());
|
assert!(!items.exhaust());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
(@bad $string:expr) => (check_form!(@opt $string, None : Option<&[(&str, &str)]>));
|
(@bad $string:expr) => (check_form!(@opt $string, None : Option<&[(&str, &str)]>));
|
||||||
($string:expr, $expected:expr) => (check_form!(@opt $string, Some($expected)));
|
($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::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use error::Error;
|
use http::RawStr;
|
||||||
use http::uri::URI;
|
|
||||||
|
|
||||||
/// Trait to create instance of some type from a form value; expected from field
|
/// Trait to create instance of some type from a form value; expected from field
|
||||||
/// types in structs deriving `FromForm`.
|
/// types in structs deriving `FromForm`.
|
||||||
|
@ -38,15 +37,16 @@ use http::uri::URI;
|
||||||
/// following structure:
|
/// following structure:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
/// # use rocket::http::RawStr;
|
||||||
/// # #[allow(dead_code)]
|
/// # #[allow(dead_code)]
|
||||||
/// struct Person<'r> {
|
/// struct Person<'r> {
|
||||||
/// name: String,
|
/// 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
|
/// The `Err` value in this case is `&RawStr` since `u16::from_form_value`
|
||||||
/// a `Result<u16, &str>`.
|
/// returns a `Result<u16, &RawStr>`.
|
||||||
///
|
///
|
||||||
/// # Provided Implementations
|
/// # Provided Implementations
|
||||||
///
|
///
|
||||||
|
@ -67,7 +67,7 @@ use http::uri::URI;
|
||||||
/// `"false"`, `"off"`, or not present. In any other case, the raw form
|
/// `"false"`, `"off"`, or not present. In any other case, the raw form
|
||||||
/// value is returned in the `Err` value.
|
/// value is returned in the `Err` value.
|
||||||
///
|
///
|
||||||
/// * **str**
|
/// * **&RawStr**
|
||||||
///
|
///
|
||||||
/// _This implementation always returns successfully._
|
/// _This implementation always returns successfully._
|
||||||
///
|
///
|
||||||
|
@ -106,14 +106,15 @@ use http::uri::URI;
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use rocket::request::FromFormValue;
|
/// use rocket::request::FromFormValue;
|
||||||
|
/// use rocket::http::RawStr;
|
||||||
///
|
///
|
||||||
/// struct AdultAge(usize);
|
/// struct AdultAge(usize);
|
||||||
///
|
///
|
||||||
/// impl<'v> FromFormValue<'v> for AdultAge {
|
/// 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> {
|
/// fn from_form_value(form_value: &'v RawStr) -> Result<AdultAge, &'v RawStr> {
|
||||||
/// match usize::from_form_value(form_value) {
|
/// match form_value.parse::<usize>() {
|
||||||
/// Ok(age) if age >= 21 => Ok(AdultAge(age)),
|
/// Ok(age) if age >= 21 => Ok(AdultAge(age)),
|
||||||
/// _ => Err(form_value),
|
/// _ => 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
|
/// Parses an instance of `Self` from an HTTP form field value or returns an
|
||||||
/// `Error` if one cannot be parsed.
|
/// `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.
|
/// 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
|
/// If this returns `None`, then the field is required. Otherwise, this
|
||||||
/// should return `Some(default_value)`. The default implementation simply
|
/// should return `Some(default_value)`. The default implementation simply
|
||||||
/// returns `None`.
|
/// returns `None`.
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Option<Self> {
|
fn default() -> Option<Self> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'v> FromFormValue<'v> for &'v str {
|
impl<'v> FromFormValue<'v> for &'v RawStr {
|
||||||
type Error = Error;
|
type Error = !;
|
||||||
|
|
||||||
// This just gives the raw string.
|
// 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)
|
Ok(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'v> FromFormValue<'v> for String {
|
impl<'v> FromFormValue<'v> for String {
|
||||||
type Error = &'v str;
|
type Error = &'v RawStr;
|
||||||
|
|
||||||
// This actually parses the value according to the standard.
|
// This actually parses the value according to the standard.
|
||||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
#[inline(always)]
|
||||||
let replaced = v.replace("+", " ");
|
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||||
match URI::percent_decode(replaced.as_bytes()) {
|
v.url_decode().map_err(|_| v)
|
||||||
Err(_) => Err(v),
|
|
||||||
Ok(string) => Ok(string.into_owned())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'v> FromFormValue<'v> for bool {
|
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> {
|
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
||||||
match v {
|
match v.as_str() {
|
||||||
"on" | "true" => Ok(true),
|
"on" | "true" => Ok(true),
|
||||||
"off" | "false" => Ok(false),
|
"off" | "false" => Ok(false),
|
||||||
_ => Err(v),
|
_ => Err(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Option<bool> {
|
fn default() -> Option<bool> {
|
||||||
Some(false)
|
Some(false)
|
||||||
}
|
}
|
||||||
|
@ -193,9 +194,11 @@ impl<'v> FromFormValue<'v> for bool {
|
||||||
macro_rules! impl_with_fromstr {
|
macro_rules! impl_with_fromstr {
|
||||||
($($T:ident),+) => ($(
|
($($T:ident),+) => ($(
|
||||||
impl<'v> FromFormValue<'v> for $T {
|
impl<'v> FromFormValue<'v> for $T {
|
||||||
type Error = &'v str;
|
type Error = &'v RawStr;
|
||||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
|
||||||
$T::from_str(v).map_err(|_| v)
|
#[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);
|
IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr);
|
||||||
|
|
||||||
impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option<T> {
|
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) {
|
match T::from_form_value(v) {
|
||||||
Ok(v) => Ok(Some(v)),
|
Ok(v) => Ok(Some(v)),
|
||||||
Err(_) => Ok(None),
|
Err(_) => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Option<Option<T>> {
|
fn default() -> Option<Option<T>> {
|
||||||
Some(None)
|
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> {
|
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) {
|
match T::from_form_value(v) {
|
||||||
ok@Ok(_) => Ok(ok),
|
ok@Ok(_) => Ok(ok),
|
||||||
e@Err(_) => Ok(e),
|
e@Err(_) => Ok(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,9 +72,10 @@ use outcome::Outcome::*;
|
||||||
/// # #![allow(deprecated, dead_code, unused_attributes)]
|
/// # #![allow(deprecated, dead_code, unused_attributes)]
|
||||||
/// # #![plugin(rocket_codegen)]
|
/// # #![plugin(rocket_codegen)]
|
||||||
/// # extern crate rocket;
|
/// # extern crate rocket;
|
||||||
|
/// # use rocket::http::RawStr;
|
||||||
/// #[derive(FromForm)]
|
/// #[derive(FromForm)]
|
||||||
/// struct UserInput<'f> {
|
/// struct UserInput<'f> {
|
||||||
/// value: &'f str
|
/// value: &'f RawStr
|
||||||
/// }
|
/// }
|
||||||
/// # fn main() { }
|
/// # fn main() { }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -88,9 +89,10 @@ use outcome::Outcome::*;
|
||||||
/// # #![plugin(rocket_codegen)]
|
/// # #![plugin(rocket_codegen)]
|
||||||
/// # extern crate rocket;
|
/// # extern crate rocket;
|
||||||
/// # use rocket::request::Form;
|
/// # use rocket::request::Form;
|
||||||
|
/// # use rocket::http::RawStr;
|
||||||
/// # #[derive(FromForm)]
|
/// # #[derive(FromForm)]
|
||||||
/// # struct UserInput<'f> {
|
/// # struct UserInput<'f> {
|
||||||
/// # value: &'f str
|
/// # value: &'f RawStr
|
||||||
/// # }
|
/// # }
|
||||||
/// #[post("/submit", data = "<user_input>")]
|
/// #[post("/submit", data = "<user_input>")]
|
||||||
/// fn submit_task<'r>(user_input: Form<'r, UserInput<'r>>) -> String {
|
/// 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 mut items = FormItems::from(long_lived_string);
|
||||||
let result = T::from_form_items(items.by_ref());
|
let result = T::from_form_items(items.by_ref());
|
||||||
if !items.exhausted() {
|
if !items.exhaust() {
|
||||||
return FormResult::Invalid(form_string);
|
return FormResult::Invalid(form_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,14 +168,15 @@ impl Rocket {
|
||||||
from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
|
from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut form_items = FormItems::from(form);
|
if let Some((key, value)) = FormItems::from(form).next() {
|
||||||
if let Some(("_method", value)) = form_items.next() {
|
if key == "_method" {
|
||||||
if let Ok(method) = value.parse() {
|
if let Ok(method) = value.parse() {
|
||||||
req.set_method(method);
|
req.set_method(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn dispatch<'s, 'r>(&'s self, request: &'r mut Request<'s>, data: Data)
|
pub(crate) fn dispatch<'s, 'r>(&'s self, request: &'r mut Request<'s>, data: Data)
|
||||||
|
|
Loading…
Reference in New Issue