#[macro_use] extern crate rocket; use rocket::request::{FromForm, FormItems, FormParseError}; use rocket::http::RawStr; fn parse<'f, T>(string: &'f str, strict: bool) -> Result> where T: FromForm<'f, Error = FormParseError<'f>> { let mut items = FormItems::from(string); let result = T::from_form(items.by_ref(), strict); if !items.exhaust() { panic!("Invalid form input."); } result } fn strict<'f, T>(string: &'f str) -> Result> where T: FromForm<'f, Error = FormParseError<'f>> { parse(string, true) } fn lenient<'f, T>(string: &'f str) -> Result> where T: FromForm<'f, Error = FormParseError<'f>> { parse(string, false) } #[derive(Debug, PartialEq, FromForm)] struct TodoTask { description: String, completed: bool } #[test] fn simple() { // Same number of arguments: simple case. let task: Option = strict("description=Hello&completed=on").ok(); assert_eq!(task, Some(TodoTask { description: "Hello".to_string(), completed: true })); // Argument in string but not in form. let task: Option = strict("other=a&description=Hello&completed=on").ok(); assert!(task.is_none()); // Ensure _method isn't required. let task: Option = strict("_method=patch&description=Hello&completed=off").ok(); assert_eq!(task, Some(TodoTask { description: "Hello".to_string(), completed: false })); } #[derive(Debug, PartialEq, FromFormValue)] enum FormOption { A, B, C } #[derive(Debug, PartialEq, FromForm)] struct FormInput<'r> { checkbox: bool, number: usize, radio: FormOption, password: &'r RawStr, textarea: String, select: FormOption, } #[derive(Debug, PartialEq, FromForm)] struct DefaultInput<'r> { arg: Option<&'r RawStr>, } #[derive(Debug, PartialEq, FromForm)] struct ManualMethod<'r> { _method: Option<&'r RawStr>, done: bool } #[derive(Debug, PartialEq, FromForm)] struct UnpresentCheckbox { checkbox: bool } #[derive(Debug, PartialEq, FromForm)] struct UnpresentCheckboxTwo<'r> { checkbox: bool, something: &'r RawStr } #[derive(Debug, PartialEq, FromForm)] struct FieldNamedV<'r> { v: &'r RawStr, } #[test] fn base_conditions() { let form_string = &[ "password=testing", "checkbox=off", "checkbox=on", "number=10", "checkbox=off", "textarea=", "select=a", "radio=c", ].join("&"); let input: Option> = strict(&form_string).ok(); assert_eq!(input, Some(FormInput { checkbox: false, number: 10, radio: FormOption::C, password: "testing".into(), textarea: "".to_string(), select: FormOption::A, })); // Argument not in string with default in form. let default: Option> = strict("").ok(); assert_eq!(default, Some(DefaultInput { arg: None })); // Ensure _method can be captured if desired. let manual: Option> = strict("_method=put&done=true").ok(); assert_eq!(manual, Some(ManualMethod { _method: Some("put".into()), done: true })); let manual: Option> = lenient("_method=put&done=true").ok(); assert_eq!(manual, Some(ManualMethod { _method: Some("put".into()), done: true })); // And ignored when not present. let manual: Option> = strict("done=true").ok(); 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 = strict("").ok(); 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> = strict("something=hello").ok(); assert_eq!(manual, Some(UnpresentCheckboxTwo { checkbox: false, something: "hello".into() })); // Check that a structure with one field `v` parses correctly. let manual: Option> = strict("v=abc").ok(); assert_eq!(manual, Some(FieldNamedV { v: "abc".into() })); } #[test] fn lenient_parsing() { // Check that a structure with one field `v` parses correctly (lenient). let manual: Option> = lenient("v=abc").ok(); assert_eq!(manual, Some(FieldNamedV { v: "abc".into() })); let manual: Option> = lenient("v=abc&a=123").ok(); assert_eq!(manual, Some(FieldNamedV { v: "abc".into() })); let manual: Option> = lenient("c=abcddef&v=abc&a=123").ok(); assert_eq!(manual, Some(FieldNamedV { v: "abc".into() })); // Check default values (bool) with lenient parsing. let manual: Option> = lenient("something=hello").ok(); assert_eq!(manual, Some(UnpresentCheckboxTwo { checkbox: false, something: "hello".into() })); let manual: Option> = lenient("hi=hi&something=hello").ok(); assert_eq!(manual, Some(UnpresentCheckboxTwo { checkbox: false, something: "hello".into() })); // Check that a missing field doesn't parse, even leniently. let manual: Option> = lenient("a=abc").ok(); assert!(manual.is_none()); let manual: Option> = lenient("_method=abc").ok(); assert!(manual.is_none()); } #[derive(Debug, PartialEq, FromForm)] struct RenamedForm { single: usize, #[form(field = "camelCase")] camel_case: String, #[form(field = "TitleCase")] title_case: String, #[form(field = "type")] field_type: isize, #[form(field = "DOUBLE")] double: String, #[form(field = "a.b")] dot: isize, #[form(field = "some space")] some_space: String, } #[test] fn field_renaming() { let form_string = &[ "single=100", "camelCase=helloThere", "TitleCase=HiHi", "type=-2", "DOUBLE=bing_bong", "a.b=123", "some space=okay" ].join("&"); let form: Option = strict(&form_string).ok(); assert_eq!(form, Some(RenamedForm { single: 100, camel_case: "helloThere".into(), title_case: "HiHi".into(), field_type: -2, double: "bing_bong".into(), dot: 123, some_space: "okay".into(), })); let form_string = &[ "single=100", "camel_case=helloThere", "TitleCase=HiHi", "type=-2", "DOUBLE=bing_bong", "dot=123", "some_space=okay" ].join("&"); let form: Option = strict(&form_string).ok(); assert!(form.is_none()); } #[derive(FromForm, Debug, PartialEq)] struct YetOneMore<'f, T> { string: &'f RawStr, other: T, } #[derive(FromForm, Debug, PartialEq)] struct Oops { base: String, a: A, b: B, c: C, } #[test] fn generics() { let form_string = &[ "string=hello", "other=00128" ].join("&"); let form: Option> = strict(&form_string).ok(); assert_eq!(form, Some(YetOneMore { string: "hello".into(), other: 128, })); let form: Option> = strict(&form_string).ok(); assert_eq!(form, Some(YetOneMore { string: "hello".into(), other: 128, })); let form: Option> = strict(&form_string).ok(); assert!(form.is_none()); let form_string = &[ "base=just%20a%20test", "a=hey%20there", "b=a", "c=811", ].join("&"); let form: Option> = strict(&form_string).ok(); assert_eq!(form, Some(Oops { base: "just a test".into(), a: "hey%20there".into(), b: FormOption::A, c: 811, })); } #[derive(Debug, PartialEq, FromForm)] struct WhoopsForm { complete: bool, other: usize, } #[test] fn form_errors() { let form: Result = strict("complete=true&other=781"); assert_eq!(form, Ok(WhoopsForm { complete: true, other: 781 })); let form: Result = strict("complete=true&other=unknown"); assert_eq!(form, Err(FormParseError::BadValue("other".into(), "unknown".into()))); let form: Result = strict("complete=unknown&other=unknown"); assert_eq!(form, Err(FormParseError::BadValue("complete".into(), "unknown".into()))); let form: Result = strict("complete=true&other=1&extra=foo"); assert_eq!(form, Err(FormParseError::Unknown("extra".into(), "foo".into()))); // Bad values take highest precedence. let form: Result = strict("complete=unknown&unknown=foo"); assert_eq!(form, Err(FormParseError::BadValue("complete".into(), "unknown".into()))); // Then unknown key/values for strict parses. let form: Result = strict("complete=true&unknown=foo"); assert_eq!(form, Err(FormParseError::Unknown("unknown".into(), "foo".into()))); // Finally, missing. let form: Result = strict("complete=true"); assert_eq!(form, Err(FormParseError::Missing("other".into()))); } #[derive(Debug, PartialEq, FromForm)] struct RawIdentForm { r#type: String, } #[test] fn raw_ident_form() { let form: Result = strict("type=a"); assert_eq!(form, Ok(RawIdentForm { r#type: "a".into() })); }