Skip to main content

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#[non_exhaustive]
51pub enum FileValue<'a> {
52    /// A part of a buffered body.
53    Buffered(&'a Part<'a>),
54    /// A stored file from an upload.
55    #[cfg(feature = "fs")]
56    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
57    Stored(crate::store::StoredFile),
58}
59
60impl core::fmt::Debug for FileValue<'_> {
61    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62        match self {
63            FileValue::Buffered(p) => f.debug_tuple("Buffered").field(p).finish(),
64            #[cfg(feature = "fs")]
65            FileValue::Stored(s) => f.debug_tuple("Stored").field(s).finish(),
66        }
67    }
68}
69
70/// How one field type takes its values.
71///
72/// The slot holds what has arrived so far, so every arity rule lives in one
73/// place: a scalar's slot is `Option<T>` (a second value is `Duplicate`,
74/// none is `Missing`), `Vec<T>` pushes, `bool` finishes a missing value as
75/// `false`. Implement it for an application type to validate while
76/// extracting.
77///
78/// # Examples
79///
80/// ```
81/// use form_net::{FromField, ValueError};
82///
83/// pub struct Slug(String);
84///
85/// impl FromField for Slug {
86///     type Slot = Option<Slug>;
87///     fn accept_text(slot: &mut Option<Slug>, value: &str) -> Result<(), ValueError> {
88///         if slot.is_some() { return Err(ValueError::Duplicate); }
89///         if value.is_empty() || value.len() > 64 || !value.bytes().all(|b| b.is_ascii_lowercase() || b == b'-') {
90///             return Err(ValueError::Invalid("lowercase letters and hyphens, 1 to 64"));
91///         }
92///         *slot = Some(Slug(value.to_owned()));
93///         Ok(())
94///     }
95///     fn finish(slot: Option<Slug>) -> Result<Slug, ValueError> {
96///         slot.ok_or(ValueError::Missing)
97///     }
98/// }
99/// ```
100pub trait FromField: Sized {
101    /// What has arrived so far.
102    type Slot: Default;
103
104    /// Takes one text value.
105    ///
106    /// # Errors
107    ///
108    /// `ExpectedFile` by default; the type's own conversion errors.
109    fn accept_text(slot: &mut Self::Slot, value: &str) -> Result<(), ValueError> {
110        let _ = (slot, value);
111        Err(ValueError::ExpectedFile)
112    }
113
114    /// Takes one file.
115    ///
116    /// # Errors
117    ///
118    /// `ExpectedText` by default; the type's own errors.
119    fn accept_file(slot: &mut Self::Slot, file: FileValue<'_>) -> Result<(), ValueError> {
120        let _ = (slot, file);
121        Err(ValueError::ExpectedText)
122    }
123
124    /// The value, once every field has arrived.
125    ///
126    /// # Errors
127    ///
128    /// `Missing`, or the type's own errors.
129    fn finish(slot: Self::Slot) -> Result<Self, ValueError>;
130}
131
132/// What [`form!`](crate::form) generates, and what a hand-written
133/// [`FromForm`] implementation provides. Reports problems as
134/// `FormError::Extract { reason, field }`.
135///
136/// # Examples
137///
138/// ```
139/// use form_net::{ExtractError, FileValue, FormBuilder, FormError, FromForm};
140///
141/// /// Accepts any field names, keeping the last `q`.
142/// #[derive(Default)]
143/// struct LastQ(Option<String>);
144///
145/// impl FormBuilder for LastQ {
146///     type Output = Query;
147///     fn text(&mut self, name: &str, value: &str) -> Result<(), FormError> {
148///         if name == "q" { self.0 = Some(value.to_owned()); }
149///         Ok(())
150///     }
151///     fn file(&mut self, _name: &str, _file: FileValue<'_>) -> Result<(), FormError> {
152///         Err(FormError::Extract { reason: ExtractError::WrongKind, field: Some("q") })
153///     }
154///     fn finish(self) -> Result<Query, FormError> {
155///         Ok(Query { q: self.0.unwrap_or_default() })
156///     }
157/// }
158///
159/// struct Query { q: String }
160/// impl FromForm for Query { type Builder = LastQ; }
161///
162/// assert_eq!(Query::from_query(b"q=a&q=b")?.q, "b");
163/// # Ok::<(), FormError>(())
164/// ```
165pub trait FormBuilder {
166    /// The struct being built.
167    type Output;
168
169    /// One text field.
170    ///
171    /// # Errors
172    ///
173    /// `Extract { .. }`.
174    fn text(&mut self, name: &str, value: &str) -> Result<(), FormError>;
175
176    /// One file.
177    ///
178    /// # Errors
179    ///
180    /// `Extract { .. }`.
181    fn file(&mut self, name: &str, file: FileValue<'_>) -> Result<(), FormError>;
182
183    /// The struct, once every field has arrived.
184    ///
185    /// # Errors
186    ///
187    /// `Extract { Missing | Invalid, .. }`.
188    fn finish(self) -> Result<Self::Output, FormError>;
189
190    /// What a file under `name` is to the struct: `Some(true)` a declared
191    /// field, `Some(false)` an ignored name, `None` an unknown one.
192    /// `FromForm::from_upload_mut` (feature `fs`) moves only declared files out of the
193    /// upload. The default says every name is declared.
194    fn file_field(&self, name: &str) -> Option<bool> {
195        let _ = name;
196        Some(true)
197    }
198
199    /// As [`finish`](Self::finish), but records every field's error in
200    /// `errors` instead of stopping at the first; `None` when any field
201    /// failed. The default records the one error `finish` returns.
202    fn finish_all(self, errors: &mut alloc::vec::Vec<FormError>) -> Option<Self::Output>
203    where
204        Self: Sized,
205    {
206        match self.finish() {
207            Ok(v) => Some(v),
208            Err(e) => {
209                errors.push(e);
210                None
211            }
212        }
213    }
214}
215
216/// Every problem with one submitted form, for showing the form again with
217/// a message beside each field: what
218/// [`FromForm::from_body_all`] returns.
219///
220/// A parse error (a malformed body, a limit) stops the parse, so it is the
221/// only error; extraction errors are collected for every field, at most one
222/// per received value plus one per missing field. No value is ever kept.
223///
224/// # Examples
225///
226/// ```
227/// use form_net::{ExtractError, FromForm};
228///
229/// form_net::form! {
230///     #[derive(Debug)]
231///     pub struct Signup { pub email: String, pub age: u8, pub tos: bool }
232/// }
233///
234/// let errors = Signup::from_body_all(b"application/x-www-form-urlencoded", b"age=300&role=admin").unwrap_err();
235/// assert_eq!(errors.len(), 3);
236/// assert_eq!(errors.field("age"), Some(ExtractError::Invalid("out of range")));
237/// assert_eq!(errors.field("email"), Some(ExtractError::Missing));
238/// assert_eq!(errors.unknown(), 1);
239/// assert_eq!(errors.status_hint(), 422);
240/// ```
241#[derive(Clone, Debug, PartialEq, Eq)]
242pub struct FormErrors(alloc::vec::Vec<FormError>);
243
244impl FormErrors {
245    /// Every error, in the order found.
246    pub fn iter(&self) -> impl Iterator<Item = &FormError> {
247        self.0.iter()
248    }
249
250    /// How many errors.
251    #[must_use]
252    pub fn len(&self) -> usize {
253        self.0.len()
254    }
255
256    /// Whether there are none (never true for a returned value).
257    #[must_use]
258    pub fn is_empty(&self) -> bool {
259        self.0.is_empty()
260    }
261
262    /// The first error.
263    #[must_use]
264    pub fn first(&self) -> Option<&FormError> {
265        self.0.first()
266    }
267
268    /// The first extraction error for the field with this Rust name.
269    #[must_use]
270    pub fn field(&self, name: &str) -> Option<ExtractError> {
271        self.0.iter().find_map(|e| match e {
272            FormError::Extract {
273                reason,
274                field: Some(f),
275            } if *f == name => Some(*reason),
276            _ => None,
277        })
278    }
279
280    /// How many received names the struct neither declares nor ignores.
281    #[must_use]
282    pub fn unknown(&self) -> usize {
283        self.0
284            .iter()
285            .filter(|e| {
286                matches!(
287                    e,
288                    FormError::Extract {
289                        reason: ExtractError::Unknown,
290                        ..
291                    }
292                )
293            })
294            .count()
295    }
296
297    /// The status hint of the first error (422 for extraction errors).
298    #[must_use]
299    pub fn status_hint(&self) -> u16 {
300        self.first().map_or(422, FormError::status_hint)
301    }
302}
303
304impl From<FormError> for FormErrors {
305    fn from(e: FormError) -> FormErrors {
306        FormErrors(alloc::vec![e])
307    }
308}
309
310impl core::fmt::Display for FormErrors {
311    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
312        match self.first() {
313            Some(e) if self.0.len() > 1 => {
314                write!(f, "{e} (and {} more)", self.0.len().saturating_sub(1))
315            }
316            Some(e) => write!(f, "{e}"),
317            None => f.write_str("no errors"),
318        }
319    }
320}
321
322#[cfg(feature = "std")]
323impl std::error::Error for FormErrors {}
324
325/// Feeds a builder and keeps going after extraction errors, recording them.
326struct Collect<'e, B> {
327    inner: B,
328    errors: &'e mut alloc::vec::Vec<FormError>,
329}
330
331impl<B: FormBuilder> FormBuilder for Collect<'_, B> {
332    type Output = B::Output;
333
334    fn text(&mut self, name: &str, value: &str) -> Result<(), FormError> {
335        match self.inner.text(name, value) {
336            Err(e @ FormError::Extract { .. }) => {
337                self.errors.push(e);
338                Ok(())
339            }
340            other => other,
341        }
342    }
343
344    fn file(&mut self, name: &str, file: FileValue<'_>) -> Result<(), FormError> {
345        match self.inner.file(name, file) {
346            Err(e @ FormError::Extract { .. }) => {
347                self.errors.push(e);
348                Ok(())
349            }
350            other => other,
351        }
352    }
353
354    fn finish(self) -> Result<B::Output, FormError> {
355        self.inner.finish()
356    }
357}
358
359/// The bytes the decoders use without allocating.
360const STACK: usize = 512;
361
362/// Feeds a urlencoded body to `builder`, decoding each name and value into a
363/// stack buffer when it fits and a heap buffer otherwise.
364fn feed_urlencoded<B: FormBuilder, I: Inspector>(
365    builder: &mut B,
366    input: &[u8],
367    limits: &Limits,
368    policy: &Policy,
369    inspector: &mut I,
370) -> Result<(), FormError> {
371    let mut name_stack = [0u8; STACK];
372    let mut value_stack = [0u8; STACK];
373    let mut heap = alloc::vec::Vec::new();
374    let fed = (|| {
375        for pair in crate::urlencoded::raw::raw_pairs(input, limits) {
376            let pair = pair?;
377            let name_len = pair.name_raw().len();
378            let value_len = pair.value_raw().len();
379            let (name_buf, value_buf): (&mut [u8], &mut [u8]) =
380                if name_len <= STACK && value_len <= STACK {
381                    (&mut name_stack, &mut value_stack)
382                } else {
383                    let needed = name_len.saturating_add(value_len);
384                    crate::wipe::wipe(&mut heap);
385                    heap.clear();
386                    if needed > heap.capacity() {
387                        // A new allocation; the old one is wiped, not freed
388                        // with a decoded value in it.
389                        let mut old =
390                            core::mem::replace(&mut heap, alloc::vec::Vec::with_capacity(needed));
391                        crate::wipe::wipe_vec(&mut old);
392                    }
393                    heap.resize(needed, 0);
394                    heap.split_at_mut(name_len)
395                };
396            let name = pair.decode_name(name_buf, policy.percent)?;
397            let value = pair.decode_value(value_buf, policy.percent)?;
398            let at = crate::error::Location::at(pair.offset());
399            match inspector.field(name, value.as_bytes()) {
400                crate::Verdict::Refuse(refusal) => return Err(FormError::Refused { refusal, at }),
401                crate::Verdict::Skip => {}
402                _ => builder.text(name, value)?,
403            }
404        }
405        Ok(())
406    })();
407    crate::wipe::wipe(&mut name_stack);
408    crate::wipe::wipe(&mut value_stack);
409    crate::wipe::wipe_vec(&mut heap);
410    fed?;
411    if let crate::Verdict::Refuse(refusal) = inspector.end() {
412        return Err(FormError::Refused {
413            refusal,
414            at: crate::error::Location::at(input.len() as u64),
415        });
416    }
417    Ok(())
418}
419
420fn feed_multipart<B: FormBuilder>(
421    builder: &mut B,
422    mp: &crate::Multipart<'_>,
423) -> Result<(), FormError> {
424    for part in mp.parts() {
425        if part.is_file() {
426            builder.file(part.name(), FileValue::Buffered(part))?;
427        } else {
428            builder.text(part.name(), part.text()?)?;
429        }
430    }
431    Ok(())
432}
433
434/// A struct that can be read from a form. Implemented by
435/// [`form!`](crate::form); every method is provided.
436///
437/// # Examples
438///
439/// ```
440/// use form_net::FromForm;
441///
442/// form_net::form! {
443///     pub struct Login {
444///         pub email: String,
445///         pub password: form_net::Secret,
446///         pub remember: bool,
447///     }
448///     ignore = ["csrf_token"];
449/// }
450///
451/// let login = Login::from_body(b"application/x-www-form-urlencoded", b"email=a%40b.example&password=x&csrf_token=t")?;
452/// assert_eq!((login.email.as_str(), login.remember), ("a@b.example", false));
453/// # Ok::<(), form_net::FormError>(())
454/// ```
455pub trait FromForm: Sized {
456    /// The builder that collects the fields.
457    type Builder: FormBuilder<Output = Self> + Default;
458
459    /// From a urlencoded body, with the default limits and policy.
460    ///
461    /// # Errors
462    ///
463    /// The parser's errors; `Extract { .. }`.
464    fn from_urlencoded(input: &[u8]) -> Result<Self, FormError> {
465        Self::from_urlencoded_with(input, &Limits::DEFAULT, &Policy::DEFAULT)
466    }
467
468    /// From a query string; one leading `?` is stripped.
469    ///
470    /// # Errors
471    ///
472    /// As [`from_urlencoded`](Self::from_urlencoded).
473    fn from_query(query: &[u8]) -> Result<Self, FormError> {
474        Self::from_urlencoded(query.strip_prefix(b"?").unwrap_or(query))
475    }
476
477    /// From a buffered `multipart/form-data` body.
478    ///
479    /// # Errors
480    ///
481    /// The parser's errors; `Extract { .. }`.
482    fn from_multipart(content_type: &[u8], body: &[u8]) -> Result<Self, FormError> {
483        let mp = crate::parse_multipart(content_type, body)?;
484        let mut b = Self::Builder::default();
485        feed_multipart(&mut b, &mp)?;
486        b.finish()
487    }
488
489    /// From either encoding, by the `Content-Type`.
490    ///
491    /// # Errors
492    ///
493    /// `UnsupportedMediaType`; the parsers' errors; `Extract { .. }`.
494    fn from_body(content_type: &[u8], body: &[u8]) -> Result<Self, FormError> {
495        Self::from_body_with(content_type, body, &Limits::DEFAULT, &Policy::DEFAULT)
496    }
497
498    /// From a urlencoded body with explicit limits and policy.
499    ///
500    /// # Errors
501    ///
502    /// As [`from_urlencoded`](Self::from_urlencoded); `InvalidLimits`.
503    fn from_urlencoded_with(
504        input: &[u8],
505        limits: &Limits,
506        policy: &Policy,
507    ) -> Result<Self, FormError> {
508        limits.validate()?;
509        let mut b = Self::Builder::default();
510        feed_urlencoded(&mut b, input, limits, policy, &mut NoInspector)?;
511        b.finish()
512    }
513
514    /// From either encoding with explicit limits and policy.
515    ///
516    /// # Errors
517    ///
518    /// As [`from_body`](Self::from_body); `InvalidLimits`.
519    fn from_body_with(
520        content_type: &[u8],
521        body: &[u8],
522        limits: &Limits,
523        policy: &Policy,
524    ) -> Result<Self, FormError> {
525        Self::from_body_inspected(content_type, body, limits, policy, &mut NoInspector)
526    }
527
528    /// From either encoding, with an inspector in the same pass: the
529    /// inspector and the struct see the same bytes.
530    ///
531    /// # Errors
532    ///
533    /// As [`from_body_with`](Self::from_body_with); `Refused`.
534    fn from_body_inspected<I: Inspector>(
535        content_type: &[u8],
536        body: &[u8],
537        limits: &Limits,
538        policy: &Policy,
539        inspector: &mut I,
540    ) -> Result<Self, FormError> {
541        let result = (|| {
542            let mut b = Self::Builder::default();
543            match FormKind::detect(content_type)? {
544                FormKind::Urlencoded => {
545                    limits.validate()?;
546                    feed_urlencoded(&mut b, body, limits, policy, inspector)?;
547                }
548                FormKind::Multipart(_) => {
549                    let mp = crate::multipart::parse_multipart_inspected_raw(
550                        content_type,
551                        body,
552                        limits,
553                        policy,
554                        inspector,
555                    )?;
556                    feed_multipart(&mut b, &mp)?;
557                }
558            }
559            b.finish()
560        })();
561        if let Err(e) = &result {
562            inspector.failed(e);
563        }
564        result
565    }
566
567    /// From either encoding, collecting every field's error instead of
568    /// stopping at the first (D-090), so a form can be shown again with a
569    /// message beside each field.
570    ///
571    /// # Errors
572    ///
573    /// [`FormErrors`]: the parse error alone, or every extraction error.
574    fn from_body_all(content_type: &[u8], body: &[u8]) -> Result<Self, FormErrors> {
575        Self::from_body_all_with(content_type, body, &Limits::DEFAULT, &Policy::DEFAULT)
576    }
577
578    /// As [`from_body_all`](Self::from_body_all), with explicit limits and
579    /// policy.
580    ///
581    /// # Errors
582    ///
583    /// As [`from_body_all`](Self::from_body_all); `InvalidLimits`.
584    fn from_body_all_with(
585        content_type: &[u8],
586        body: &[u8],
587        limits: &Limits,
588        policy: &Policy,
589    ) -> Result<Self, FormErrors> {
590        let mut errors = alloc::vec::Vec::new();
591        let mut b = Collect {
592            inner: Self::Builder::default(),
593            errors: &mut errors,
594        };
595        match FormKind::detect(content_type)? {
596            FormKind::Urlencoded => {
597                limits.validate()?;
598                feed_urlencoded(&mut b, body, limits, policy, &mut NoInspector)?;
599            }
600            FormKind::Multipart(_) => {
601                let mp = crate::parse_multipart_with(content_type, body, limits, policy)?;
602                feed_multipart(&mut b, &mp)?;
603            }
604        }
605        let Collect { inner, errors: _ } = b;
606        let value = inner.finish_all(&mut errors);
607        match value {
608            Some(v) if errors.is_empty() => Ok(v),
609            _ => Err(FormErrors(errors)),
610        }
611    }
612
613    /// From an upload that stays usable (D-090): text fields are read by
614    /// reference, and only the files the struct declares are moved out of
615    /// it; files under `ignore = [..]` names stay in the upload for the
616    /// caller. Unknown names are refused as in
617    /// [`from_upload`](Self::from_upload). On error, files already moved
618    /// into the builder are dropped (deleted).
619    ///
620    /// # Errors
621    ///
622    /// `Extract { .. }`.
623    ///
624    /// # Examples
625    ///
626    /// ```
627    /// use form_net::FromForm;
628    ///
629    /// form_net::form! {
630    ///     pub struct Post { pub title: String, pub cover: form_net::StoredFile }
631    ///     ignore = ["attachment"];
632    /// }
633    ///
634    /// 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--";
635    /// let mut upload = form_net::read_multipart(b"multipart/form-data; boundary=X", &body[..])?;
636    /// let post = Post::from_upload_mut(&mut upload)?;
637    /// assert_eq!((post.title.as_str(), post.cover.len()), ("Hi", 1));
638    /// assert_eq!(upload.field("title"), Some("Hi"));
639    /// assert!(upload.file("cover").is_none() && upload.file("attachment").is_some());
640    /// # Ok::<(), form_net::FormError>(())
641    /// ```
642    #[cfg(feature = "fs")]
643    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
644    fn from_upload_mut(upload: &mut crate::store::Upload) -> Result<Self, FormError> {
645        let mut b = Self::Builder::default();
646        for (name, value) in upload.iter() {
647            b.text(name, value)?;
648        }
649        let files = upload.files_mut();
650        let mut i = 0usize;
651        while let Some(file) = files.get(i) {
652            match b.file_field(file.name()) {
653                Some(true) => {
654                    let file = files.remove(i);
655                    let name = alloc::string::String::from(file.name());
656                    b.file(&name, FileValue::Stored(file))?;
657                }
658                Some(false) => i = i.wrapping_add(1),
659                None => return Err(__unknown()),
660            }
661        }
662        b.finish()
663    }
664
665    /// From a finished upload: text fields as text, files moved into the
666    /// struct as `StoredFile`s.
667    ///
668    /// # Errors
669    ///
670    /// `Extract { .. }`.
671    #[cfg(feature = "fs")]
672    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
673    fn from_upload(upload: crate::store::Upload) -> Result<Self, FormError> {
674        let (fields, files) = upload.into_parts();
675        let mut b = Self::Builder::default();
676        for (name, value) in fields.iter() {
677            b.text(name, value)?;
678        }
679        for file in files {
680            let name = alloc::string::String::from(file.name());
681            b.file(&name, FileValue::Stored(file))?;
682        }
683        b.finish()
684    }
685}
686
687/// Maps a field's [`ValueError`] to the error `form!` returns. Not part of
688/// the API: used by the macro expansion.
689#[doc(hidden)]
690#[must_use]
691pub fn __value_error(e: ValueError, field: &'static str) -> FormError {
692    let reason = match e {
693        ValueError::Missing => ExtractError::Missing,
694        ValueError::Duplicate => ExtractError::Duplicate,
695        ValueError::Invalid(why) => ExtractError::Invalid(why),
696        _ => ExtractError::WrongKind,
697    };
698    FormError::Extract {
699        reason,
700        field: Some(field),
701    }
702}
703
704/// Records a field's error at `finish_all`, leaving out a `Missing` for a
705/// field that already failed when its value arrived. Not part of the API.
706#[doc(hidden)]
707pub fn __push_field_error(
708    errors: &mut alloc::vec::Vec<FormError>,
709    e: ValueError,
710    field: &'static str,
711) {
712    let seen = errors
713        .iter()
714        .any(|x| matches!(x, FormError::Extract { field: Some(f), .. } if *f == field));
715    if !(seen && e == ValueError::Missing) {
716        errors.push(__value_error(e, field));
717    }
718}
719
720/// The error for a name the struct does not declare. Not part of the API.
721#[doc(hidden)]
722#[must_use]
723pub const fn __unknown() -> FormError {
724    FormError::Extract {
725        reason: ExtractError::Unknown,
726        field: None,
727    }
728}