Rocket/codegen/tests/run-pass/derive_form.rs
Sergio Benitez ed429cd487 Change FromForm signature. Emit 422 form errors on bad form strings.
This commit changes the way Rocket parses form items. In particular, it now
(liberally) validates form strings, returning a Bad Request on malformed inputs
and Unprocessable Entity on bad parses.

The 'FormItems' iterator was modified to accomodate this. The iterator is now
initialized using 'from': 'FormItems::from(form_string)'. The iterator can be
queried to check for a complete parse using either 'completed()' or
'exhausted()', the latter of which will consume valid keys/values and return
true only if the entire string was consumed.

The 'FromForm' trait now takes a mutable borrow to a 'FormItems' iterator.

The 'Form' and 'FormForm' implementation for 'Form' were modified to use the new
iterfaces and check for 'exhausted' after a parse, returning a Bad Request error
if the iterator cannot be exhausted.

Resolves #46.
2017-02-01 18:22:51 -08:00

144 lines
3.6 KiB
Rust

#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]
extern crate rocket;
use rocket::request::{FromForm, FromFormValue, FormItems};
#[derive(Debug, PartialEq, FromForm)]
struct TodoTask {
description: String,
completed: bool
}
// TODO: Make deriving `FromForm` for this enum possible.
#[derive(Debug, PartialEq)]
enum FormOption {
A, B, C
}
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 {
"a" => FormOption::A,
"b" => FormOption::B,
"c" => FormOption::C,
_ => return Err(v)
};
Ok(variant)
}
}
#[derive(Debug, PartialEq, FromForm)]
struct FormInput<'r> {
checkbox: bool,
number: usize,
radio: FormOption,
password: &'r str,
textarea: String,
select: FormOption,
}
#[derive(Debug, PartialEq, FromForm)]
struct DefaultInput<'r> {
arg: Option<&'r str>,
}
#[derive(Debug, PartialEq, FromForm)]
struct ManualMethod<'r> {
_method: Option<&'r str>,
done: bool
}
#[derive(Debug, PartialEq, FromForm)]
struct UnpresentCheckbox {
checkbox: bool
}
#[derive(Debug, PartialEq, FromForm)]
struct UnpresentCheckboxTwo<'r> {
checkbox: bool,
something: &'r str
}
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() {
panic!("Invalid form input.");
}
result.ok()
}
fn main() {
// Same number of arguments: simple case.
let task: Option<TodoTask> = parse("description=Hello&completed=on");
assert_eq!(task, Some(TodoTask {
description: "Hello".to_string(),
completed: true
}));
// Argument in string but not in form.
let task: Option<TodoTask> = parse("other=a&description=Hello&completed=on");
assert!(task.is_none());
// Ensure _method isn't required.
let task: Option<TodoTask> = parse("_method=patch&description=Hello&completed=off");
assert_eq!(task, Some(TodoTask {
description: "Hello".to_string(),
completed: false
}));
let form_string = &[
"password=testing", "checkbox=off", "checkbox=on", "number=10",
"checkbox=off", "textarea=", "select=a", "radio=c",
].join("&");
let input: Option<FormInput> = parse(&form_string);
assert_eq!(input, Some(FormInput {
checkbox: false,
number: 10,
radio: FormOption::C,
password: "testing",
textarea: "".to_string(),
select: FormOption::A,
}));
// Argument not in string with default in form.
let default: Option<DefaultInput> = parse("");
assert_eq!(default, Some(DefaultInput {
arg: None
}));
// Ensure _method can be captured if desired.
let manual: Option<ManualMethod> = parse("_method=put&done=true");
assert_eq!(manual, Some(ManualMethod {
_method: Some("put"),
done: true
}));
// And ignored when not present.
let manual: Option<ManualMethod> = parse("done=true");
assert_eq!(manual, Some(ManualMethod {
_method: None,
done: true
}));
// Check that a `bool` value that isn't in the form is marked as `false`.
let manual: Option<UnpresentCheckbox> = parse("");
assert_eq!(manual, Some(UnpresentCheckbox {
checkbox: false
}));
// Check that a `bool` value that isn't in the form is marked as `false`.
let manual: Option<UnpresentCheckboxTwo> = parse("something=hello");
assert_eq!(manual, Some(UnpresentCheckboxTwo {
checkbox: false,
something: "hello"
}));
}