1 #[macro_use] extern crate rocket;
2
3 use rocket::request::{FromForm, FormItems, FormParseError};
4 use rocket::http::RawStr;
5
parse<'f, T>(string: &'f str, strict: bool) -> Result<T, FormParseError<'f>> where T: FromForm<'f, Error = FormParseError<'f>>6 fn parse<'f, T>(string: &'f str, strict: bool) -> Result<T, FormParseError<'f>>
7 where T: FromForm<'f, Error = FormParseError<'f>>
8 {
9 let mut items = FormItems::from(string);
10 let result = T::from_form(items.by_ref(), strict);
11 if !items.exhaust() {
12 panic!("Invalid form input.");
13 }
14
15 result
16 }
17
strict<'f, T>(string: &'f str) -> Result<T, FormParseError<'f>> where T: FromForm<'f, Error = FormParseError<'f>>18 fn strict<'f, T>(string: &'f str) -> Result<T, FormParseError<'f>>
19 where T: FromForm<'f, Error = FormParseError<'f>>
20 {
21 parse(string, true)
22 }
23
lenient<'f, T>(string: &'f str) -> Result<T, FormParseError<'f>> where T: FromForm<'f, Error = FormParseError<'f>>24 fn lenient<'f, T>(string: &'f str) -> Result<T, FormParseError<'f>>
25 where T: FromForm<'f, Error = FormParseError<'f>>
26 {
27 parse(string, false)
28 }
29
30 #[derive(Debug, PartialEq, FromForm)]
31 struct TodoTask {
32 description: String,
33 completed: bool
34 }
35
36 #[test]
simple()37 fn simple() {
38 // Same number of arguments: simple case.
39 let task: Option<TodoTask> = strict("description=Hello&completed=on").ok();
40 assert_eq!(task, Some(TodoTask {
41 description: "Hello".to_string(),
42 completed: true
43 }));
44
45 // Argument in string but not in form.
46 let task: Option<TodoTask> = strict("other=a&description=Hello&completed=on").ok();
47 assert!(task.is_none());
48
49 // Ensure _method isn't required.
50 let task: Option<TodoTask> = strict("_method=patch&description=Hello&completed=off").ok();
51 assert_eq!(task, Some(TodoTask {
52 description: "Hello".to_string(),
53 completed: false
54 }));
55 }
56
57 #[derive(Debug, PartialEq, FromFormValue)]
58 enum FormOption {
59 A, B, C
60 }
61
62 #[derive(Debug, PartialEq, FromForm)]
63 struct FormInput<'r> {
64 checkbox: bool,
65 number: usize,
66 radio: FormOption,
67 password: &'r RawStr,
68 textarea: String,
69 select: FormOption,
70 }
71
72 #[derive(Debug, PartialEq, FromForm)]
73 struct DefaultInput<'r> {
74 arg: Option<&'r RawStr>,
75 }
76
77 #[derive(Debug, PartialEq, FromForm)]
78 struct ManualMethod<'r> {
79 _method: Option<&'r RawStr>,
80 done: bool
81 }
82
83 #[derive(Debug, PartialEq, FromForm)]
84 struct UnpresentCheckbox {
85 checkbox: bool
86 }
87
88 #[derive(Debug, PartialEq, FromForm)]
89 struct UnpresentCheckboxTwo<'r> {
90 checkbox: bool,
91 something: &'r RawStr
92 }
93
94 #[derive(Debug, PartialEq, FromForm)]
95 struct FieldNamedV<'r> {
96 v: &'r RawStr,
97 }
98
99 #[test]
base_conditions()100 fn base_conditions() {
101 let form_string = &[
102 "password=testing", "checkbox=off", "checkbox=on", "number=10",
103 "checkbox=off", "textarea=", "select=a", "radio=c",
104 ].join("&");
105
106 let input: Option<FormInput<'_>> = strict(&form_string).ok();
107 assert_eq!(input, Some(FormInput {
108 checkbox: false,
109 number: 10,
110 radio: FormOption::C,
111 password: "testing".into(),
112 textarea: "".to_string(),
113 select: FormOption::A,
114 }));
115
116 // Argument not in string with default in form.
117 let default: Option<DefaultInput<'_>> = strict("").ok();
118 assert_eq!(default, Some(DefaultInput {
119 arg: None
120 }));
121
122 // Ensure _method can be captured if desired.
123 let manual: Option<ManualMethod<'_>> = strict("_method=put&done=true").ok();
124 assert_eq!(manual, Some(ManualMethod {
125 _method: Some("put".into()),
126 done: true
127 }));
128
129 let manual: Option<ManualMethod<'_>> = lenient("_method=put&done=true").ok();
130 assert_eq!(manual, Some(ManualMethod {
131 _method: Some("put".into()),
132 done: true
133 }));
134
135 // And ignored when not present.
136 let manual: Option<ManualMethod<'_>> = strict("done=true").ok();
137 assert_eq!(manual, Some(ManualMethod {
138 _method: None,
139 done: true
140 }));
141
142 // Check that a `bool` value that isn't in the form is marked as `false`.
143 let manual: Option<UnpresentCheckbox> = strict("").ok();
144 assert_eq!(manual, Some(UnpresentCheckbox {
145 checkbox: false
146 }));
147
148 // Check that a `bool` value that isn't in the form is marked as `false`.
149 let manual: Option<UnpresentCheckboxTwo<'_>> = strict("something=hello").ok();
150 assert_eq!(manual, Some(UnpresentCheckboxTwo {
151 checkbox: false,
152 something: "hello".into()
153 }));
154
155 // Check that a structure with one field `v` parses correctly.
156 let manual: Option<FieldNamedV<'_>> = strict("v=abc").ok();
157 assert_eq!(manual, Some(FieldNamedV {
158 v: "abc".into()
159 }));
160
161 }
162
163 #[test]
lenient_parsing()164 fn lenient_parsing() {
165 // Check that a structure with one field `v` parses correctly (lenient).
166 let manual: Option<FieldNamedV<'_>> = lenient("v=abc").ok();
167 assert_eq!(manual, Some(FieldNamedV { v: "abc".into() }));
168
169 let manual: Option<FieldNamedV<'_>> = lenient("v=abc&a=123").ok();
170 assert_eq!(manual, Some(FieldNamedV { v: "abc".into() }));
171
172 let manual: Option<FieldNamedV<'_>> = lenient("c=abcddef&v=abc&a=123").ok();
173 assert_eq!(manual, Some(FieldNamedV { v: "abc".into() }));
174
175 // Check default values (bool) with lenient parsing.
176 let manual: Option<UnpresentCheckboxTwo<'_>> = lenient("something=hello").ok();
177 assert_eq!(manual, Some(UnpresentCheckboxTwo {
178 checkbox: false,
179 something: "hello".into()
180 }));
181
182 let manual: Option<UnpresentCheckboxTwo<'_>> = lenient("hi=hi&something=hello").ok();
183 assert_eq!(manual, Some(UnpresentCheckboxTwo {
184 checkbox: false,
185 something: "hello".into()
186 }));
187
188 // Check that a missing field doesn't parse, even leniently.
189 let manual: Option<FieldNamedV<'_>> = lenient("a=abc").ok();
190 assert!(manual.is_none());
191
192 let manual: Option<FieldNamedV<'_>> = lenient("_method=abc").ok();
193 assert!(manual.is_none());
194 }
195
196 #[derive(Debug, PartialEq, FromForm)]
197 struct RenamedForm {
198 single: usize,
199 #[form(field = "camelCase")]
200 camel_case: String,
201 #[form(field = "TitleCase")]
202 title_case: String,
203 #[form(field = "type")]
204 field_type: isize,
205 #[form(field = "DOUBLE")]
206 double: String,
207 #[form(field = "a.b")]
208 dot: isize,
209 #[form(field = "some space")]
210 some_space: String,
211 }
212
213 #[test]
field_renaming()214 fn field_renaming() {
215 let form_string = &[
216 "single=100", "camelCase=helloThere", "TitleCase=HiHi", "type=-2",
217 "DOUBLE=bing_bong", "a.b=123", "some space=okay"
218 ].join("&");
219
220 let form: Option<RenamedForm> = strict(&form_string).ok();
221 assert_eq!(form, Some(RenamedForm {
222 single: 100,
223 camel_case: "helloThere".into(),
224 title_case: "HiHi".into(),
225 field_type: -2,
226 double: "bing_bong".into(),
227 dot: 123,
228 some_space: "okay".into(),
229 }));
230
231 let form_string = &[
232 "single=100", "camel_case=helloThere", "TitleCase=HiHi", "type=-2",
233 "DOUBLE=bing_bong", "dot=123", "some_space=okay"
234 ].join("&");
235
236 let form: Option<RenamedForm> = strict(&form_string).ok();
237 assert!(form.is_none());
238 }
239
240 #[derive(FromForm, Debug, PartialEq)]
241 struct YetOneMore<'f, T> {
242 string: &'f RawStr,
243 other: T,
244 }
245
246 #[derive(FromForm, Debug, PartialEq)]
247 struct Oops<A, B, C> {
248 base: String,
249 a: A,
250 b: B,
251 c: C,
252 }
253
254 #[test]
generics()255 fn generics() {
256 let form_string = &[
257 "string=hello", "other=00128"
258 ].join("&");
259
260 let form: Option<YetOneMore<'_, usize>> = strict(&form_string).ok();
261 assert_eq!(form, Some(YetOneMore {
262 string: "hello".into(),
263 other: 128,
264 }));
265
266 let form: Option<YetOneMore<'_, u8>> = strict(&form_string).ok();
267 assert_eq!(form, Some(YetOneMore {
268 string: "hello".into(),
269 other: 128,
270 }));
271
272 let form: Option<YetOneMore<'_, i8>> = strict(&form_string).ok();
273 assert!(form.is_none());
274
275 let form_string = &[
276 "base=just%20a%20test", "a=hey%20there", "b=a", "c=811",
277 ].join("&");
278
279 let form: Option<Oops<&RawStr, FormOption, usize>> = strict(&form_string).ok();
280 assert_eq!(form, Some(Oops {
281 base: "just a test".into(),
282 a: "hey%20there".into(),
283 b: FormOption::A,
284 c: 811,
285 }));
286 }
287
288 #[derive(Debug, PartialEq, FromForm)]
289 struct WhoopsForm {
290 complete: bool,
291 other: usize,
292 }
293
294 #[test]
form_errors()295 fn form_errors() {
296 let form: Result<WhoopsForm, _> = strict("complete=true&other=781");
297 assert_eq!(form, Ok(WhoopsForm { complete: true, other: 781 }));
298
299 let form: Result<WhoopsForm, _> = strict("complete=true&other=unknown");
300 assert_eq!(form, Err(FormParseError::BadValue("other".into(), "unknown".into())));
301
302 let form: Result<WhoopsForm, _> = strict("complete=unknown&other=unknown");
303 assert_eq!(form, Err(FormParseError::BadValue("complete".into(), "unknown".into())));
304
305 let form: Result<WhoopsForm, _> = strict("complete=true&other=1&extra=foo");
306 assert_eq!(form, Err(FormParseError::Unknown("extra".into(), "foo".into())));
307
308 // Bad values take highest precedence.
309 let form: Result<WhoopsForm, _> = strict("complete=unknown&unknown=foo");
310 assert_eq!(form, Err(FormParseError::BadValue("complete".into(), "unknown".into())));
311
312 // Then unknown key/values for strict parses.
313 let form: Result<WhoopsForm, _> = strict("complete=true&unknown=foo");
314 assert_eq!(form, Err(FormParseError::Unknown("unknown".into(), "foo".into())));
315
316 // Finally, missing.
317 let form: Result<WhoopsForm, _> = strict("complete=true");
318 assert_eq!(form, Err(FormParseError::Missing("other".into())));
319 }
320