form_net/extract/mod.rs
1//! Typed extraction: plain Rust structs filled from a form, without serde
2//! and without a procedural macro.
3//!
4//! [`form!`](crate::form) generates a struct and its [`FromForm`]
5//! implementation; each field type implements [`FromField`]. Unknown names
6//! are refused, a scalar given twice is refused, a missing required field is
7//! refused, and every error names the field by its Rust-side name.
8
9mod file;
10mod macros;
11mod secret;
12mod value;
13
14pub use file::FileData;
15pub use secret::Secret;
16
17use crate::error::{ExtractError, FormError};
18use crate::hook::{Inspector, NoInspector};
19use crate::kind::FormKind;
20use crate::limits::{Limits, Policy};
21use crate::multipart::Part;
22
23/// Why one field's value did not convert. [`form!`](crate::form) turns it
24/// into `FormError::Extract` with the field's name.
25///
26/// # Examples
27///
28/// ```
29/// use form_net::{FromField, ValueError};
30///
31/// let mut slot = <u8 as FromField>::Slot::default();
32/// assert_eq!(u8::accept_text(&mut slot, "300"), Err(ValueError::Invalid("out of range")));
33/// ```
34#[non_exhaustive]
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum ValueError {
37 /// A required field is absent.
38 Missing,
39 /// A single-valued field was given twice.
40 Duplicate,
41 /// The value does not convert; the text says why.
42 Invalid(&'static str),
43 /// Text was given to a file field.
44 ExpectedFile,
45 /// A file was given to a text field.
46 ExpectedText,
47}
48
49/// A file field's value.
50///
51/// # Examples
52///
53/// ```
54/// use form_net::{FileData, FileValue, FromField};
55///
56/// let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--")?;
57/// let mut slot = <FileData as FromField>::Slot::default();
58/// FileData::accept_file(&mut slot, FileValue::Buffered(mp.file("f").unwrap())).unwrap();
59/// assert_eq!(FileData::finish(slot).unwrap().bytes(), b"hi");
60/// # Ok::<(), form_net::FormError>(())
61/// ```
62#[non_exhaustive]
63pub enum FileValue<'a> {
64 /// A part of a buffered body.
65 Buffered(&'a Part<'a>),
66 /// A stored file from an upload.
67 #[cfg(feature = "fs")]
68 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
69 Stored(crate::store::StoredFile),
70}
71
72impl core::fmt::Debug for FileValue<'_> {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 match self {
75 FileValue::Buffered(p) => f.debug_tuple("Buffered").field(p).finish(),
76 #[cfg(feature = "fs")]
77 FileValue::Stored(s) => f.debug_tuple("Stored").field(s).finish(),
78 }
79 }
80}
81
82/// How one field type takes its values.
83///
84/// The slot holds what has arrived so far, so every arity rule lives in one
85/// place: a scalar's slot is `Option<T>` (a second value is `Duplicate`,
86/// none is `Missing`), `Vec<T>` pushes, `bool` finishes a missing value as
87/// `false`. Implement it for an application type to validate while
88/// extracting.
89///
90/// # Examples
91///
92/// ```
93/// use form_net::{FromField, ValueError};
94///
95/// pub struct Slug(String);
96///
97/// impl FromField for Slug {
98/// type Slot = Option<Slug>;
99/// fn accept_text(slot: &mut Option<Slug>, value: &str) -> Result<(), ValueError> {
100/// if slot.is_some() { return Err(ValueError::Duplicate); }
101/// if value.is_empty() || value.len() > 64 || !value.bytes().all(|b| b.is_ascii_lowercase() || b == b'-') {
102/// return Err(ValueError::Invalid("lowercase letters and hyphens, 1 to 64"));
103/// }
104/// *slot = Some(Slug(value.to_owned()));
105/// Ok(())
106/// }
107/// fn finish(slot: Option<Slug>) -> Result<Slug, ValueError> {
108/// slot.ok_or(ValueError::Missing)
109/// }
110/// }
111/// ```
112pub trait FromField: Sized {
113 /// What has arrived so far.
114 type Slot: Default;
115
116 /// Takes one text value.
117 ///
118 /// # Errors
119 ///
120 /// `ExpectedFile` by default; the type's own conversion errors.
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use form_net::{FromField, ValueError};
126 ///
127 /// let mut slot = <u16 as FromField>::Slot::default();
128 /// assert_eq!(u16::accept_text(&mut slot, "8080"), Ok(()));
129 /// assert_eq!(u16::accept_text(&mut slot, "80"), Err(ValueError::Duplicate));
130 /// assert_eq!(u16::finish(slot), Ok(8080));
131 /// ```
132 fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
133 let _ = (slot, value);
134 Err(ValueError::ExpectedFile)
135 }
136
137 /// Takes one file.
138 ///
139 /// # Errors
140 ///
141 /// `ExpectedText` by default; the type's own errors.
142 ///
143 /// # Examples
144 ///
145 /// ```
146 /// use form_net::{FileValue, FromField, ValueError};
147 ///
148 /// let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--")?;
149 /// let mut slot = <String as FromField>::Slot::default();
150 /// let file = FileValue::Buffered(mp.file("f").unwrap());
151 /// // A text field type refuses a file.
152 /// assert_eq!(String::accept_file(&mut slot, file), Err(ValueError::ExpectedText));
153 /// # Ok::<(), form_net::FormError>(())
154 /// ```
155 fn accept_file(slot: &mut Self::Slot, file: FileValue<'_>) -> Result<(), ValueError> {
156 let _ = (slot, file);
157 Err(ValueError::ExpectedText)
158 }
159
160 /// The value, once every field has arrived.
161 ///
162 /// # Errors
163 ///
164 /// `Missing`, or the type's own errors.
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use form_net::{FromField, ValueError};
170 ///
171 /// let mut slot = <Vec<u8> as FromField>::Slot::default();
172 /// <Vec<u8>>::accept_text(&mut slot, "1").unwrap();
173 /// <Vec<u8>>::accept_text(&mut slot, "2").unwrap();
174 /// assert_eq!(<Vec<u8>>::finish(slot), Ok(vec![1, 2]));
175 /// assert_eq!(String::finish(None), Err(ValueError::Missing));
176 /// ```
177 fn finish(slot: Self::Slot) -> Result<Self, ValueError>;
178}
179
180/// What [`form!`](crate::form) generates, and what a hand-written
181/// [`FromForm`] implementation provides. Reports problems as
182/// `FormError::Extract { reason, field }`.
183///
184/// # Examples
185///
186/// ```
187/// use form_net::{ExtractError, FileValue, FormBuilder, FormError, FromForm};
188///
189/// /// Accepts any field names, keeping the last `q`.
190/// #[derive(Default)]
191/// struct LastQ(Option<String>);
192///
193/// impl FormBuilder for LastQ {
194/// type Output = Query;
195/// fn text(&mut self, name: &str, value: &str) -> Result<(), FormError> {
196/// if name == "q" { self.0 = Some(value.to_owned()); }
197/// Ok(())
198/// }
199/// fn file(&mut self, _name: &str, _file: FileValue<'_>) -> Result<(), FormError> {
200/// Err(FormError::Extract { reason: ExtractError::WrongKind, field: Some("q") })
201/// }
202/// fn finish(self) -> Result<Query, FormError> {
203/// Ok(Query { q: self.0.unwrap_or_default() })
204/// }
205/// }
206///
207/// struct Query { q: String }
208/// impl FromForm for Query { type Builder = LastQ; }
209///
210/// assert_eq!(Query::from_query(b"q=a&q=b")?.q, "b");
211/// # Ok::<(), FormError>(())
212/// ```
213pub trait FormBuilder {
214 /// The struct being built.
215 type Output;
216
217 /// One text field.
218 ///
219 /// # Errors
220 ///
221 /// `Extract { .. }`.
222 ///
223 /// # Examples
224 ///
225 /// ```
226 /// use form_net::{FormBuilder, FromForm};
227 ///
228 /// form_net::form! { #[derive(Debug)] pub struct Search { pub q: String } }
229 ///
230 /// let mut b = <Search as FromForm>::Builder::default();
231 /// b.text("q", "rust")?;
232 /// assert_eq!(b.text("page", "2").unwrap_err().code(), "extract.unknown");
233 /// # Ok::<(), form_net::FormError>(())
234 /// ```
235 fn text(&mut self, name: &str, value: &str) -> Result<(), FormError>;
236
237 /// One file.
238 ///
239 /// # Errors
240 ///
241 /// `Extract { .. }`.
242 ///
243 /// # Examples
244 ///
245 /// ```
246 /// use form_net::{FileValue, FormBuilder, FromForm};
247 ///
248 /// form_net::form! { pub struct Avatar { pub f: form_net::FileData } }
249 ///
250 /// let mp = form_net::parse_multipart(b"multipart/form-data; boundary=X", b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhi\r\n--X--")?;
251 /// let mut b = <Avatar as FromForm>::Builder::default();
252 /// b.file("f", FileValue::Buffered(mp.file("f").unwrap()))?;
253 /// assert_eq!(b.finish()?.f.bytes(), b"hi");
254 /// # Ok::<(), form_net::FormError>(())
255 /// ```
256 fn file(&mut self, name: &str, file: FileValue<'_>) -> Result<(), FormError>;
257
258 /// The struct, once every field has arrived.
259 ///
260 /// # Errors
261 ///
262 /// `Extract { Missing | Invalid, .. }`.
263 ///
264 /// # Examples
265 ///
266 /// ```
267 /// use form_net::{ExtractError, FormBuilder, FormError, FromForm};
268 ///
269 /// form_net::form! { #[derive(Debug)] pub struct Search { pub q: String } }
270 ///
271 /// let b = <Search as FromForm>::Builder::default();
272 /// let err = b.finish().unwrap_err();
273 /// assert!(matches!(err, FormError::Extract { reason: ExtractError::Missing, field: Some("q") }));
274 /// ```
275 fn finish(self) -> Result<Self::Output, FormError>;
276
277 /// What a file under `name` is to the struct: `Some(true)` a declared
278 /// field, `Some(false)` an ignored name, `None` an unknown one.
279 /// `FromForm::from_upload_mut` (feature `fs`) moves only declared files out of the
280 /// upload. The default says every name is declared.
281 ///
282 /// # Examples
283 ///
284 /// ```
285 /// use form_net::{FormBuilder, FromForm};
286 ///
287 /// form_net::form! {
288 /// pub struct Post { pub cover: form_net::FileData }
289 /// ignore = ["note"];
290 /// }
291 ///
292 /// let b = <Post as FromForm>::Builder::default();
293 /// assert_eq!(b.file_field("cover"), Some(true));
294 /// assert_eq!(b.file_field("note"), Some(false));
295 /// assert_eq!(b.file_field("other"), None);
296 /// ```
297 fn file_field(&self, name: &str) -> Option<bool> {
298 let _ = name;
299 Some(true)
300 }
301
302 /// As [`finish`](Self::finish), but records every field's error in
303 /// `errors` instead of stopping at the first; `None` when any field
304 /// failed. The default records the one error `finish` returns.
305 ///
306 /// # Examples
307 ///
308 /// ```
309 /// use form_net::{FormBuilder, FromForm};
310 ///
311 /// form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
312 ///
313 /// let mut errors = Vec::new();
314 /// let b = <Signup as FromForm>::Builder::default();
315 /// assert!(b.finish_all(&mut errors).is_none());
316 /// // Both missing fields are reported, not only the first.
317 /// assert_eq!(errors.len(), 2);
318 /// ```
319 fn finish_all(self, errors: &mut alloc::vec::Vec<FormError>) -> Option<Self::Output>
320 where
321 Self: Sized,
322 {
323 match self.finish() {
324 Ok(v) => Some(v),
325 Err(e) => {
326 errors.push(e);
327 None
328 }
329 }
330 }
331}
332
333/// Every problem with one submitted form, for showing the form again with
334/// a message beside each field: what
335/// [`FromForm::from_body_all`] returns.
336///
337/// A parse error (a malformed body, a limit) stops the parse, so it is the
338/// only error; extraction errors are collected for every field, at most one
339/// per received value plus one per missing field. No value is ever kept.
340///
341/// # Examples
342///
343/// ```
344/// use form_net::{ExtractError, FromForm};
345///
346/// form_net::form! {
347/// #[derive(Debug)]
348/// pub struct Signup { pub email: String, pub age: u8, pub tos: bool }
349/// }
350///
351/// let errors = Signup::from_body_all(b"application/x-www-form-urlencoded", b"age=300&role=admin").unwrap_err();
352/// assert_eq!(errors.len(), 3);
353/// assert_eq!(errors.field("age"), Some(ExtractError::Invalid("out of range")));
354/// assert_eq!(errors.field("email"), Some(ExtractError::Missing));
355/// assert_eq!(errors.unknown(), 1);
356/// assert_eq!(errors.status_hint(), 422);
357/// ```
358#[derive(Clone, Debug, PartialEq, Eq)]
359pub struct FormErrors(alloc::vec::Vec<FormError>);
360
361impl FormErrors {
362 /// Every error, in the order found.
363 ///
364 /// # Examples
365 ///
366 /// ```
367 /// use form_net::{ExtractError, FromForm};
368 ///
369 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
370 /// let ct = b"application/x-www-form-urlencoded";
371 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
372 /// let codes: Vec<_> = errors.iter().map(|e| e.code()).collect();
373 /// assert_eq!(codes, ["extract.invalid", "extract.unknown", "extract.missing"]);
374 /// ```
375 pub fn iter(&self) -> impl Iterator<Item = &FormError> {
376 self.0.iter()
377 }
378
379 /// How many errors.
380 ///
381 /// # Examples
382 ///
383 /// ```
384 /// use form_net::{ExtractError, FromForm};
385 ///
386 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
387 /// let ct = b"application/x-www-form-urlencoded";
388 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
389 /// assert_eq!(errors.len(), 3);
390 /// ```
391 #[must_use]
392 pub fn len(&self) -> usize {
393 self.0.len()
394 }
395
396 /// Whether there are none (never true for a returned value).
397 ///
398 /// # Examples
399 ///
400 /// ```
401 /// use form_net::{ExtractError, FromForm};
402 ///
403 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
404 /// let ct = b"application/x-www-form-urlencoded";
405 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
406 /// assert!(!errors.is_empty());
407 /// ```
408 #[must_use]
409 pub fn is_empty(&self) -> bool {
410 self.0.is_empty()
411 }
412
413 /// The first error.
414 ///
415 /// # Examples
416 ///
417 /// ```
418 /// use form_net::{ExtractError, FromForm};
419 ///
420 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
421 /// let ct = b"application/x-www-form-urlencoded";
422 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
423 /// assert_eq!(errors.first().map(|e| e.code()), Some("extract.invalid"));
424 /// ```
425 #[must_use]
426 pub fn first(&self) -> Option<&FormError> {
427 self.0.first()
428 }
429
430 /// The first extraction error for the field with this Rust name.
431 ///
432 /// # Examples
433 ///
434 /// ```
435 /// use form_net::{ExtractError, FromForm};
436 ///
437 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
438 /// let ct = b"application/x-www-form-urlencoded";
439 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
440 /// assert_eq!(errors.field("age"), Some(ExtractError::Invalid("out of range")));
441 /// assert_eq!(errors.field("email"), Some(ExtractError::Missing));
442 /// ```
443 #[must_use]
444 pub fn field(&self, name: &str) -> Option<ExtractError> {
445 self.0.iter().find_map(|e| match e {
446 FormError::Extract {
447 reason,
448 field: Some(f),
449 } if *f == name => Some(*reason),
450 _ => None,
451 })
452 }
453
454 /// How many received names the struct neither declares nor ignores.
455 ///
456 /// # Examples
457 ///
458 /// ```
459 /// use form_net::{ExtractError, FromForm};
460 ///
461 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
462 /// let ct = b"application/x-www-form-urlencoded";
463 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
464 /// // `role` is neither a field nor an ignored name.
465 /// assert_eq!(errors.unknown(), 1);
466 /// ```
467 #[must_use]
468 pub fn unknown(&self) -> usize {
469 self.0
470 .iter()
471 .filter(|e| {
472 matches!(
473 e,
474 FormError::Extract {
475 reason: ExtractError::Unknown,
476 ..
477 }
478 )
479 })
480 .count()
481 }
482
483 /// The status hint of the first error (422 for extraction errors).
484 ///
485 /// # Examples
486 ///
487 /// ```
488 /// use form_net::{ExtractError, FromForm};
489 ///
490 /// # form_net::form! { #[derive(Debug)] pub struct Signup { pub email: String, pub age: u8 } }
491 /// let ct = b"application/x-www-form-urlencoded";
492 /// let errors = Signup::from_body_all(ct, b"age=300&role=admin").unwrap_err();
493 /// assert_eq!(errors.status_hint(), 422);
494 /// ```
495 #[must_use]
496 pub fn status_hint(&self) -> u16 {
497 self.first().map_or(422, FormError::status_hint)
498 }
499}
500
501impl From<FormError> for FormErrors {
502 fn from(e: FormError) -> FormErrors {
503 FormErrors(alloc::vec![e])
504 }
505}
506
507impl core::fmt::Display for FormErrors {
508 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
509 match self.first() {
510 Some(e) if self.0.len() > 1 => {
511 write!(f, "{e} (and {} more)", self.0.len().saturating_sub(1))
512 }
513 Some(e) => write!(f, "{e}"),
514 None => f.write_str("no errors"),
515 }
516 }
517}
518
519#[cfg(feature = "std")]
520impl std::error::Error for FormErrors {}
521
522/// Feeds a builder and keeps going after extraction errors, recording them.
523struct Collect<'e, B> {
524 inner: B,
525 errors: &'e mut alloc::vec::Vec<FormError>,
526}
527
528impl<B: FormBuilder> FormBuilder for Collect<'_, B> {
529 type Output = B::Output;
530
531 fn text(&mut self, name: &str, value: &str) -> Result<(), FormError> {
532 match self.inner.text(name, value) {
533 Err(e @ FormError::Extract { .. }) => {
534 self.errors.push(e);
535 Ok(())
536 }
537 other => other,
538 }
539 }
540
541 fn file(&mut self, name: &str, file: FileValue<'_>) -> Result<(), FormError> {
542 match self.inner.file(name, file) {
543 Err(e @ FormError::Extract { .. }) => {
544 self.errors.push(e);
545 Ok(())
546 }
547 other => other,
548 }
549 }
550
551 fn finish(self) -> Result<B::Output, FormError> {
552 self.inner.finish()
553 }
554}
555
556/// The bytes the decoders use without allocating.
557const STACK: usize = 512;
558
559/// Feeds a urlencoded body to `builder`, decoding each name and value into a
560/// stack buffer when it fits and a heap buffer otherwise.
561fn feed_urlencoded<B: FormBuilder, I: Inspector>(
562 builder: &mut B,
563 input: &[u8],
564 limits: &Limits,
565 policy: &Policy,
566 inspector: &mut I,
567) -> Result<(), FormError> {
568 let mut name_stack = [0u8; STACK];
569 let mut value_stack = [0u8; STACK];
570 // The most of each stack buffer any pair wrote: only that is wiped.
571 let (mut name_hi, mut value_hi) = (0usize, 0usize);
572 let mut heap = alloc::vec::Vec::new();
573 // One UTF-8 pass over the body; plain names and values are cut from it.
574 let whole = core::str::from_utf8(input).ok();
575 let fed = (|| {
576 for pair in crate::urlencoded::raw::raw_pairs(input, limits) {
577 let pair = pair?;
578 // Nothing to decode (the usual case): borrow, no copy.
579 if let (Some(name), Some(value)) = (pair.name_text(whole), pair.value_text(whole)) {
580 let at = crate::error::Location::at(pair.offset());
581 match inspector.field(name, value.as_bytes()) {
582 crate::Verdict::Refuse(refusal) => {
583 return Err(FormError::Refused { refusal, at });
584 }
585 crate::Verdict::Skip => {}
586 _ => builder.text(name, value)?,
587 }
588 continue;
589 }
590 let name_len = pair.name_raw().len();
591 let value_len = pair.value_raw().len();
592 let (name_buf, value_buf): (&mut [u8], &mut [u8]) =
593 if name_len <= STACK && value_len <= STACK {
594 name_hi = name_hi.max(name_len);
595 value_hi = value_hi.max(value_len);
596 (&mut name_stack, &mut value_stack)
597 } else {
598 let needed = name_len.saturating_add(value_len);
599 crate::wipe::wipe(&mut heap);
600 heap.clear();
601 if needed > heap.capacity() {
602 // A new allocation; the old one is wiped, not freed
603 // with a decoded value in it.
604 let mut old =
605 core::mem::replace(&mut heap, alloc::vec::Vec::with_capacity(needed));
606 crate::wipe::wipe_vec(&mut old);
607 }
608 heap.resize(needed, 0);
609 heap.split_at_mut(name_len)
610 };
611 let name = pair.decode_name(name_buf, policy.percent)?;
612 let value = pair.decode_value(value_buf, policy.percent)?;
613 let at = crate::error::Location::at(pair.offset());
614 match inspector.field(name, value.as_bytes()) {
615 crate::Verdict::Refuse(refusal) => return Err(FormError::Refused { refusal, at }),
616 crate::Verdict::Skip => {}
617 _ => builder.text(name, value)?,
618 }
619 }
620 Ok(())
621 })();
622 crate::wipe::wipe(name_stack.get_mut(..name_hi).unwrap_or(&mut []));
623 crate::wipe::wipe(value_stack.get_mut(..value_hi).unwrap_or(&mut []));
624 crate::wipe::wipe_vec(&mut heap);
625 fed?;
626 if let crate::Verdict::Refuse(refusal) = inspector.end() {
627 return Err(FormError::Refused {
628 refusal,
629 at: crate::error::Location::at(input.len() as u64),
630 });
631 }
632 Ok(())
633}
634
635fn feed_multipart<B: FormBuilder>(
636 builder: &mut B,
637 mp: &crate::Multipart<'_>,
638) -> Result<(), FormError> {
639 for part in mp.parts() {
640 if part.is_file() {
641 builder.file(part.name(), FileValue::Buffered(part))?;
642 } else {
643 builder.text(part.name(), part.text()?)?;
644 }
645 }
646 Ok(())
647}
648
649/// A struct that can be read from a form. Implemented by
650/// [`form!`](crate::form); every method is provided.
651///
652/// # Examples
653///
654/// ```
655/// use form_net::FromForm;
656///
657/// form_net::form! {
658/// pub struct Login {
659/// pub email: String,
660/// pub password: form_net::Secret,
661/// pub remember: bool,
662/// }
663/// ignore = ["csrf_token"];
664/// }
665///
666/// let login = Login::from_body(b"application/x-www-form-urlencoded", b"email=a%40b.example&password=x&csrf_token=t")?;
667/// assert_eq!((login.email.as_str(), login.remember), ("a@b.example", false));
668/// # Ok::<(), form_net::FormError>(())
669/// ```
670pub trait FromForm: Sized {
671 /// The builder that collects the fields.
672 type Builder: FormBuilder<Output = Self> + Default;
673
674 /// From a urlencoded body, with the default limits and policy.
675 ///
676 /// # Errors
677 ///
678 /// The parser's errors; `Extract { .. }`.
679 ///
680 /// # Examples
681 ///
682 /// ```
683 /// use form_net::FromForm;
684 ///
685 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
686 /// let s = Search::from_urlencoded(b"q=rust+forms&page=2")?;
687 /// assert_eq!((s.q.as_str(), s.page), ("rust forms", 2));
688 /// # Ok::<(), form_net::FormError>(())
689 /// ```
690 fn from_urlencoded(input: &[u8]) -> Result<Self, FormError> {
691 Self::from_urlencoded_with(input, &Limits::DEFAULT, &Policy::DEFAULT)
692 }
693
694 /// From a query string; one leading `?` is stripped.
695 ///
696 /// # Errors
697 ///
698 /// As [`from_urlencoded`](Self::from_urlencoded).
699 ///
700 /// # Examples
701 ///
702 /// ```
703 /// use form_net::FromForm;
704 ///
705 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
706 /// let s = Search::from_query(b"?q=rust&page=1")?;
707 /// assert_eq!((s.q.as_str(), s.page), ("rust", 1));
708 /// # Ok::<(), form_net::FormError>(())
709 /// ```
710 fn from_query(query: &[u8]) -> Result<Self, FormError> {
711 Self::from_urlencoded(query.strip_prefix(b"?").unwrap_or(query))
712 }
713
714 /// From a buffered `multipart/form-data` body.
715 ///
716 /// # Errors
717 ///
718 /// The parser's errors; `Extract { .. }`.
719 ///
720 /// # Examples
721 ///
722 /// ```
723 /// use form_net::FromForm;
724 ///
725 /// # form_net::form! { pub struct Note { pub title: String } }
726 /// let body = b"--X\r\nContent-Disposition: form-data; name=title\r\n\r\nHi\r\n--X--";
727 /// let note = Note::from_multipart(b"multipart/form-data; boundary=X", body)?;
728 /// assert_eq!(note.title, "Hi");
729 /// # Ok::<(), form_net::FormError>(())
730 /// ```
731 fn from_multipart(content_type: &[u8], body: &[u8]) -> Result<Self, FormError> {
732 let mp = crate::parse_multipart(content_type, body)?;
733 let mut b = Self::Builder::default();
734 feed_multipart(&mut b, &mp)?;
735 b.finish()
736 }
737
738 /// From either encoding, by the `Content-Type`.
739 ///
740 /// # Errors
741 ///
742 /// `UnsupportedMediaType`; the parsers' errors; `Extract { .. }`.
743 ///
744 /// # Examples
745 ///
746 /// ```
747 /// use form_net::FromForm;
748 ///
749 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
750 /// let s = Search::from_body(b"application/x-www-form-urlencoded", b"q=a&page=3")?;
751 /// assert_eq!(s.page, 3);
752 /// let err = Search::from_body(b"application/json", b"{}").unwrap_err();
753 /// assert_eq!(err.code(), "unsupported_media_type");
754 /// # Ok::<(), form_net::FormError>(())
755 /// ```
756 fn from_body(content_type: &[u8], body: &[u8]) -> Result<Self, FormError> {
757 Self::from_body_with(content_type, body, &Limits::DEFAULT, &Policy::DEFAULT)
758 }
759
760 /// From a urlencoded body with explicit limits and policy.
761 ///
762 /// # Errors
763 ///
764 /// As [`from_urlencoded`](Self::from_urlencoded); `InvalidLimits`.
765 ///
766 /// # Examples
767 ///
768 /// ```
769 /// use form_net::{FromForm, Limits, Policy};
770 ///
771 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
772 /// let limits = Limits::DEFAULT.with_max_body_bytes(16);
773 /// let r = Search::from_urlencoded_with(b"q=a-long-search-term&page=1", &limits, &Policy::DEFAULT);
774 /// assert_eq!(r.unwrap_err().code(), "limit.body");
775 /// ```
776 fn from_urlencoded_with(
777 input: &[u8],
778 limits: &Limits,
779 policy: &Policy,
780 ) -> Result<Self, FormError> {
781 limits.validate()?;
782 let mut b = Self::Builder::default();
783 feed_urlencoded(&mut b, input, limits, policy, &mut NoInspector)?;
784 b.finish()
785 }
786
787 /// From either encoding with explicit limits and policy.
788 ///
789 /// # Errors
790 ///
791 /// As [`from_body`](Self::from_body); `InvalidLimits`.
792 ///
793 /// # Examples
794 ///
795 /// ```
796 /// use form_net::{FromForm, Limits, Policy};
797 ///
798 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
799 /// let limits = Limits::DEFAULT.with_max_fields(1);
800 /// let ct = b"application/x-www-form-urlencoded";
801 /// let err = Search::from_body_with(ct, b"q=a&page=1", &limits, &Policy::DEFAULT).unwrap_err();
802 /// assert_eq!(err.code(), "limit.fields");
803 /// ```
804 fn from_body_with(
805 content_type: &[u8],
806 body: &[u8],
807 limits: &Limits,
808 policy: &Policy,
809 ) -> Result<Self, FormError> {
810 Self::from_body_inspected(content_type, body, limits, policy, &mut NoInspector)
811 }
812
813 /// From either encoding, with an inspector in the same pass: the
814 /// inspector and the struct see the same bytes.
815 ///
816 /// # Errors
817 ///
818 /// As [`from_body_with`](Self::from_body_with); `Refused`.
819 ///
820 /// # Examples
821 ///
822 /// ```
823 /// use form_net::{FromForm, Inspector, Limits, Policy, Refusal, Verdict};
824 ///
825 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
826 /// struct NoAdmin;
827 /// impl Inspector for NoAdmin {
828 /// fn field(&mut self, name: &str, _value: &[u8]) -> Verdict {
829 /// if name == "admin" { Verdict::Refuse(Refusal::new(403, "app.admin")) } else { Verdict::Continue }
830 /// }
831 /// }
832 ///
833 /// let ct = b"application/x-www-form-urlencoded";
834 /// let r = Search::from_body_inspected(ct, b"admin=1&q=a", &Limits::DEFAULT, &Policy::DEFAULT, &mut NoAdmin);
835 /// assert_eq!(r.unwrap_err().code(), "app.admin");
836 /// ```
837 fn from_body_inspected<I: Inspector>(
838 content_type: &[u8],
839 body: &[u8],
840 limits: &Limits,
841 policy: &Policy,
842 inspector: &mut I,
843 ) -> Result<Self, FormError> {
844 let result = (|| {
845 let mut b = Self::Builder::default();
846 match FormKind::detect(content_type)? {
847 FormKind::Urlencoded => {
848 limits.validate()?;
849 feed_urlencoded(&mut b, body, limits, policy, inspector)?;
850 }
851 FormKind::Multipart(_) => {
852 let mp = crate::multipart::parse_multipart_inspected_raw(
853 content_type,
854 body,
855 limits,
856 policy,
857 inspector,
858 )?;
859 feed_multipart(&mut b, &mp)?;
860 }
861 }
862 b.finish()
863 })();
864 if let Err(e) = &result {
865 inspector.failed(e);
866 }
867 result
868 }
869
870 /// From either encoding, collecting every field's error instead of
871 /// stopping at the first (D-090), so a form can be shown again with a
872 /// message beside each field.
873 ///
874 /// # Errors
875 ///
876 /// [`FormErrors`]: the parse error alone, or every extraction error.
877 ///
878 /// # Examples
879 ///
880 /// ```
881 /// use form_net::{ExtractError, FromForm};
882 ///
883 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
884 /// let errors = Search::from_body_all(b"application/x-www-form-urlencoded", b"page=x").unwrap_err();
885 /// assert_eq!(errors.field("page"), Some(ExtractError::Invalid("not a whole number")));
886 /// assert_eq!(errors.field("q"), Some(ExtractError::Missing));
887 /// ```
888 fn from_body_all(content_type: &[u8], body: &[u8]) -> Result<Self, FormErrors> {
889 Self::from_body_all_with(content_type, body, &Limits::DEFAULT, &Policy::DEFAULT)
890 }
891
892 /// As [`from_body_all`](Self::from_body_all), with explicit limits and
893 /// policy.
894 ///
895 /// # Errors
896 ///
897 /// As [`from_body_all`](Self::from_body_all); `InvalidLimits`.
898 ///
899 /// # Examples
900 ///
901 /// ```
902 /// use form_net::{FromForm, Limits, Policy};
903 ///
904 /// # form_net::form! { #[derive(Debug)] pub struct Search { pub q: String, pub page: u32 } }
905 /// let limits = Limits::DEFAULT.with_max_body_bytes(8);
906 /// let ct = b"application/x-www-form-urlencoded";
907 /// let errors = Search::from_body_all_with(ct, b"q=a&page=10", &limits, &Policy::DEFAULT).unwrap_err();
908 /// // A parse error stops the parse and is the only error.
909 /// assert_eq!((errors.len(), errors.status_hint()), (1, 413));
910 /// ```
911 fn from_body_all_with(
912 content_type: &[u8],
913 body: &[u8],
914 limits: &Limits,
915 policy: &Policy,
916 ) -> Result<Self, FormErrors> {
917 let mut errors = alloc::vec::Vec::new();
918 let mut b = Collect {
919 inner: Self::Builder::default(),
920 errors: &mut errors,
921 };
922 match FormKind::detect(content_type)? {
923 FormKind::Urlencoded => {
924 limits.validate()?;
925 feed_urlencoded(&mut b, body, limits, policy, &mut NoInspector)?;
926 }
927 FormKind::Multipart(_) => {
928 let mp = crate::parse_multipart_with(content_type, body, limits, policy)?;
929 feed_multipart(&mut b, &mp)?;
930 }
931 }
932 let Collect { inner, errors: _ } = b;
933 let value = inner.finish_all(&mut errors);
934 match value {
935 Some(v) if errors.is_empty() => Ok(v),
936 _ => Err(FormErrors(errors)),
937 }
938 }
939
940 /// From an upload that stays usable (D-090): text fields are read by
941 /// reference, and only the files the struct declares are moved out of
942 /// it; files under `ignore = [..]` names stay in the upload for the
943 /// caller. Unknown names are refused as in
944 /// [`from_upload`](Self::from_upload). On error, files already moved
945 /// into the builder are dropped (deleted).
946 ///
947 /// # Errors
948 ///
949 /// `Extract { .. }`.
950 ///
951 /// # Examples
952 ///
953 /// ```
954 /// use form_net::FromForm;
955 ///
956 /// form_net::form! {
957 /// pub struct Post { pub title: String, pub cover: form_net::StoredFile }
958 /// ignore = ["attachment"];
959 /// }
960 ///
961 /// let body = b"--X\r\nContent-Disposition: form-data; name=title\r\n\r\nHi\r\n--X\r\nContent-Disposition: form-data; name=cover; filename=c.png\r\n\r\nC\r\n--X\r\nContent-Disposition: form-data; name=attachment; filename=a.pdf\r\n\r\nA\r\n--X--";
962 /// let mut upload = form_net::read_multipart(b"multipart/form-data; boundary=X", &body[..])?;
963 /// let post = Post::from_upload_mut(&mut upload)?;
964 /// assert_eq!((post.title.as_str(), post.cover.len()), ("Hi", 1));
965 /// assert_eq!(upload.field("title"), Some("Hi"));
966 /// assert!(upload.file("cover").is_none() && upload.file("attachment").is_some());
967 /// # Ok::<(), form_net::FormError>(())
968 /// ```
969 #[cfg(feature = "fs")]
970 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
971 fn from_upload_mut(upload: &mut crate::store::Upload) -> Result<Self, FormError> {
972 let mut b = Self::Builder::default();
973 for (name, value) in upload.iter() {
974 b.text(name, value)?;
975 }
976 let files = upload.files_mut();
977 let mut i = 0usize;
978 while let Some(file) = files.get(i) {
979 match b.file_field(file.name()) {
980 Some(true) => {
981 let file = files.remove(i);
982 let name = alloc::string::String::from(file.name());
983 b.file(&name, FileValue::Stored(file))?;
984 }
985 Some(false) => i = i.wrapping_add(1),
986 None => return Err(__unknown()),
987 }
988 }
989 b.finish()
990 }
991
992 /// From a finished upload: text fields as text, files moved into the
993 /// struct as `StoredFile`s.
994 ///
995 /// # Errors
996 ///
997 /// `Extract { .. }`.
998 ///
999 /// # Examples
1000 ///
1001 /// ```
1002 /// use form_net::FromForm;
1003 ///
1004 /// form_net::form! { pub struct Post { pub title: String, pub cover: form_net::StoredFile } }
1005 ///
1006 /// let body = b"--X\r\nContent-Disposition: form-data; name=title\r\n\r\nHi\r\n--X\r\nContent-Disposition: form-data; name=cover; filename=c.png\r\n\r\nC\r\n--X--";
1007 /// let upload = form_net::read_multipart(b"multipart/form-data; boundary=X", &body[..])?;
1008 /// let post = Post::from_upload(upload)?;
1009 /// assert_eq!((post.title.as_str(), post.cover.len()), ("Hi", 1));
1010 /// # Ok::<(), form_net::FormError>(())
1011 /// ```
1012 #[cfg(feature = "fs")]
1013 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1014 fn from_upload(upload: crate::store::Upload) -> Result<Self, FormError> {
1015 let (fields, files) = upload.into_parts();
1016 let mut b = Self::Builder::default();
1017 for (name, value) in fields.iter() {
1018 b.text(name, value)?;
1019 }
1020 for file in files {
1021 let name = alloc::string::String::from(file.name());
1022 b.file(&name, FileValue::Stored(file))?;
1023 }
1024 b.finish()
1025 }
1026}
1027
1028/// Maps a field's [`ValueError`] to the error `form!` returns. Not part of
1029/// the API: used by the macro expansion.
1030#[doc(hidden)]
1031#[must_use]
1032pub fn __value_error(e: ValueError, field: &'static str) -> FormError {
1033 let reason = match e {
1034 ValueError::Missing => ExtractError::Missing,
1035 ValueError::Duplicate => ExtractError::Duplicate,
1036 ValueError::Invalid(why) => ExtractError::Invalid(why),
1037 _ => ExtractError::WrongKind,
1038 };
1039 FormError::Extract {
1040 reason,
1041 field: Some(field),
1042 }
1043}
1044
1045/// Records a field's error at `finish_all`, leaving out a `Missing` for a
1046/// field that already failed when its value arrived. Not part of the API.
1047#[doc(hidden)]
1048pub fn __push_field_error(
1049 errors: &mut alloc::vec::Vec<FormError>,
1050 e: ValueError,
1051 field: &'static str,
1052) {
1053 let seen = errors
1054 .iter()
1055 .any(|x| matches!(x, FormError::Extract { field: Some(f), .. } if *f == field));
1056 if !(seen && e == ValueError::Missing) {
1057 errors.push(__value_error(e, field));
1058 }
1059}
1060
1061/// Whether two of `names` are equal, byte for byte: `form!` refuses to
1062/// compile a struct whose second field of a name could never receive a
1063/// value (D-138). Quadratic, at compile time only. Not part of the API.
1064#[doc(hidden)]
1065#[must_use]
1066pub const fn __repeated_wire_name(mut names: &[&str]) -> bool {
1067 while let [first, rest @ ..] = names {
1068 let mut others = rest;
1069 while let [other, tail @ ..] = others {
1070 if bytes_equal(first.as_bytes(), other.as_bytes()) {
1071 return true;
1072 }
1073 others = tail;
1074 }
1075 names = rest;
1076 }
1077 false
1078}
1079
1080/// `a == b` in a const context.
1081const fn bytes_equal(mut a: &[u8], mut b: &[u8]) -> bool {
1082 if a.len() != b.len() {
1083 return false;
1084 }
1085 while let ([x, ra @ ..], [y, rb @ ..]) = (a, b) {
1086 if *x != *y {
1087 return false;
1088 }
1089 a = ra;
1090 b = rb;
1091 }
1092 true
1093}
1094
1095/// The error for a name the struct does not declare. Not part of the API.
1096#[doc(hidden)]
1097#[must_use]
1098pub const fn __unknown() -> FormError {
1099 FormError::Extract {
1100 reason: ExtractError::Unknown,
1101 field: None,
1102 }
1103}