2017-08-29 03:14:59 +00:00
|
|
|
#![feature(plugin, decl_macro, custom_derive)]
|
2016-09-30 08:25:07 +00:00
|
|
|
#![plugin(rocket_codegen)]
|
|
|
|
|
|
|
|
extern crate rocket;
|
|
|
|
|
2017-02-02 00:34:49 +00:00
|
|
|
use rocket::request::{FromForm, FromFormValue, FormItems};
|
2017-03-31 06:06:53 +00:00
|
|
|
use rocket::http::RawStr;
|
2016-09-30 08:25:07 +00:00
|
|
|
|
|
|
|
#[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;
|
|
|
|
|
2017-03-31 06:06:53 +00:00
|
|
|
fn from_form_value(v: &'v RawStr) -> Result<Self, Self::Error> {
|
|
|
|
let variant = match v.as_str() {
|
2016-09-30 08:25:07 +00:00
|
|
|
"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,
|
2017-03-31 06:06:53 +00:00
|
|
|
password: &'r RawStr,
|
2016-09-30 08:25:07 +00:00
|
|
|
textarea: String,
|
|
|
|
select: FormOption,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, FromForm)]
|
|
|
|
struct DefaultInput<'r> {
|
2017-03-31 06:06:53 +00:00
|
|
|
arg: Option<&'r RawStr>,
|
2016-09-30 08:25:07 +00:00
|
|
|
}
|
|
|
|
|
2016-12-26 03:32:32 +00:00
|
|
|
#[derive(Debug, PartialEq, FromForm)]
|
|
|
|
struct ManualMethod<'r> {
|
2017-03-31 06:06:53 +00:00
|
|
|
_method: Option<&'r RawStr>,
|
2016-12-26 03:32:32 +00:00
|
|
|
done: bool
|
|
|
|
}
|
|
|
|
|
2017-02-01 08:12:11 +00:00
|
|
|
#[derive(Debug, PartialEq, FromForm)]
|
|
|
|
struct UnpresentCheckbox {
|
|
|
|
checkbox: bool
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, FromForm)]
|
|
|
|
struct UnpresentCheckboxTwo<'r> {
|
|
|
|
checkbox: bool,
|
2017-03-31 06:06:53 +00:00
|
|
|
something: &'r RawStr
|
2017-02-01 08:12:11 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 22:54:44 +00:00
|
|
|
#[derive(Debug, PartialEq, FromForm)]
|
|
|
|
struct FieldNamedV<'r> {
|
|
|
|
v: &'r RawStr,
|
|
|
|
}
|
|
|
|
|
2017-06-18 08:59:22 +00:00
|
|
|
fn parse<'f, T: FromForm<'f>>(string: &'f str, strict: bool) -> Option<T> {
|
2017-02-02 00:34:49 +00:00
|
|
|
let mut items = FormItems::from(string);
|
2017-06-18 08:59:22 +00:00
|
|
|
let result = T::from_form(items.by_ref(), strict);
|
2017-03-31 06:06:53 +00:00
|
|
|
if !items.exhaust() {
|
2017-02-02 00:34:49 +00:00
|
|
|
panic!("Invalid form input.");
|
|
|
|
}
|
|
|
|
|
|
|
|
result.ok()
|
|
|
|
}
|
|
|
|
|
2017-06-18 08:59:22 +00:00
|
|
|
fn strict<'f, T: FromForm<'f>>(string: &'f str) -> Option<T> {
|
|
|
|
parse(string, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn lenient<'f, T: FromForm<'f>>(string: &'f str) -> Option<T> {
|
|
|
|
parse(string, false)
|
|
|
|
}
|
|
|
|
|
2017-09-10 08:19:38 +00:00
|
|
|
#[test]
|
2016-09-30 08:25:07 +00:00
|
|
|
fn main() {
|
|
|
|
// Same number of arguments: simple case.
|
2017-06-18 08:59:22 +00:00
|
|
|
let task: Option<TodoTask> = strict("description=Hello&completed=on");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(task, Some(TodoTask {
|
2016-09-30 08:25:07 +00:00
|
|
|
description: "Hello".to_string(),
|
|
|
|
completed: true
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Argument in string but not in form.
|
2017-06-18 08:59:22 +00:00
|
|
|
let task: Option<TodoTask> = strict("other=a&description=Hello&completed=on");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert!(task.is_none());
|
2016-09-30 08:25:07 +00:00
|
|
|
|
2016-12-26 03:32:32 +00:00
|
|
|
// Ensure _method isn't required.
|
2017-06-18 08:59:22 +00:00
|
|
|
let task: Option<TodoTask> = strict("_method=patch&description=Hello&completed=off");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(task, Some(TodoTask {
|
2016-12-26 03:32:32 +00:00
|
|
|
description: "Hello".to_string(),
|
|
|
|
completed: false
|
|
|
|
}));
|
|
|
|
|
2016-09-30 08:25:07 +00:00
|
|
|
let form_string = &[
|
|
|
|
"password=testing", "checkbox=off", "checkbox=on", "number=10",
|
|
|
|
"checkbox=off", "textarea=", "select=a", "radio=c",
|
|
|
|
].join("&");
|
|
|
|
|
2017-06-18 08:59:22 +00:00
|
|
|
let input: Option<FormInput> = strict(&form_string);
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(input, Some(FormInput {
|
2016-09-30 08:25:07 +00:00
|
|
|
checkbox: false,
|
|
|
|
number: 10,
|
|
|
|
radio: FormOption::C,
|
2017-03-31 06:06:53 +00:00
|
|
|
password: "testing".into(),
|
2016-09-30 08:25:07 +00:00
|
|
|
textarea: "".to_string(),
|
|
|
|
select: FormOption::A,
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Argument not in string with default in form.
|
2017-06-18 08:59:22 +00:00
|
|
|
let default: Option<DefaultInput> = strict("");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(default, Some(DefaultInput {
|
2016-09-30 08:25:07 +00:00
|
|
|
arg: None
|
|
|
|
}));
|
2016-12-26 03:32:32 +00:00
|
|
|
|
|
|
|
// Ensure _method can be captured if desired.
|
2017-06-18 08:59:22 +00:00
|
|
|
let manual: Option<ManualMethod> = strict("_method=put&done=true");
|
|
|
|
assert_eq!(manual, Some(ManualMethod {
|
|
|
|
_method: Some("put".into()),
|
|
|
|
done: true
|
|
|
|
}));
|
|
|
|
|
|
|
|
let manual: Option<ManualMethod> = lenient("_method=put&done=true");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(manual, Some(ManualMethod {
|
2017-03-31 06:06:53 +00:00
|
|
|
_method: Some("put".into()),
|
2016-12-26 03:32:32 +00:00
|
|
|
done: true
|
|
|
|
}));
|
|
|
|
|
|
|
|
// And ignored when not present.
|
2017-06-18 08:59:22 +00:00
|
|
|
let manual: Option<ManualMethod> = strict("done=true");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(manual, Some(ManualMethod {
|
2016-12-26 03:32:32 +00:00
|
|
|
_method: None,
|
|
|
|
done: true
|
|
|
|
}));
|
2017-02-01 08:12:11 +00:00
|
|
|
|
|
|
|
// Check that a `bool` value that isn't in the form is marked as `false`.
|
2017-06-18 08:59:22 +00:00
|
|
|
let manual: Option<UnpresentCheckbox> = strict("");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(manual, Some(UnpresentCheckbox {
|
2017-02-01 08:12:11 +00:00
|
|
|
checkbox: false
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Check that a `bool` value that isn't in the form is marked as `false`.
|
2017-06-18 08:59:22 +00:00
|
|
|
let manual: Option<UnpresentCheckboxTwo> = strict("something=hello");
|
2017-02-02 00:34:49 +00:00
|
|
|
assert_eq!(manual, Some(UnpresentCheckboxTwo {
|
2017-02-01 08:12:11 +00:00
|
|
|
checkbox: false,
|
2017-03-31 06:06:53 +00:00
|
|
|
something: "hello".into()
|
2017-02-01 08:12:11 +00:00
|
|
|
}));
|
2017-04-17 22:54:44 +00:00
|
|
|
|
|
|
|
// Check that a structure with one field `v` parses correctly.
|
2017-06-18 08:59:22 +00:00
|
|
|
let manual: Option<FieldNamedV> = strict("v=abc");
|
2017-04-17 22:54:44 +00:00
|
|
|
assert_eq!(manual, Some(FieldNamedV {
|
|
|
|
v: "abc".into()
|
|
|
|
}));
|
2017-06-18 08:59:22 +00:00
|
|
|
|
|
|
|
// Check that a structure with one field `v` parses correctly (lenient).
|
|
|
|
let manual: Option<FieldNamedV> = lenient("v=abc");
|
|
|
|
assert_eq!(manual, Some(FieldNamedV { v: "abc".into() }));
|
|
|
|
|
|
|
|
let manual: Option<FieldNamedV> = lenient("v=abc&a=123");
|
|
|
|
assert_eq!(manual, Some(FieldNamedV { v: "abc".into() }));
|
|
|
|
|
|
|
|
let manual: Option<FieldNamedV> = lenient("c=abcddef&v=abc&a=123");
|
|
|
|
assert_eq!(manual, Some(FieldNamedV { v: "abc".into() }));
|
|
|
|
|
|
|
|
// Check default values (bool) with lenient parsing.
|
|
|
|
let manual: Option<UnpresentCheckboxTwo> = lenient("something=hello");
|
|
|
|
assert_eq!(manual, Some(UnpresentCheckboxTwo {
|
|
|
|
checkbox: false,
|
|
|
|
something: "hello".into()
|
|
|
|
}));
|
|
|
|
|
|
|
|
let manual: Option<UnpresentCheckboxTwo> = lenient("hi=hi&something=hello");
|
|
|
|
assert_eq!(manual, Some(UnpresentCheckboxTwo {
|
|
|
|
checkbox: false,
|
|
|
|
something: "hello".into()
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Check that a missing field doesn't parse, even leniently.
|
|
|
|
let manual: Option<FieldNamedV> = lenient("a=abc");
|
|
|
|
assert!(manual.is_none());
|
|
|
|
|
|
|
|
let manual: Option<FieldNamedV> = lenient("_method=abc");
|
|
|
|
assert!(manual.is_none());
|
2016-09-30 08:25:07 +00:00
|
|
|
}
|