Skip to main content

form_net/extract/
macros.rs

1//! The `form!` macro.
2
3/// Declares a struct and its [`FromForm`](crate::FromForm) implementation.
4///
5/// - The struct is emitted exactly as written (attributes, doc comments,
6///   visibility); it may not be generic and must have named fields.
7/// - A field's wire name is its Rust name; `= "wire-name"` after the type
8///   overrides it. Bracket names such as `tags[]` are plain names, matched
9///   byte for byte, never expanded into a structure.
10/// - The optional `ignore = [ ... ];` after the struct lists names that are
11///   accepted and dropped (the anti-forgery field and `_charset_` are the
12///   usual ones). Every other undeclared name is refused.
13/// - Field types implement [`FromField`](crate::FromField); see the value
14///   rules in `docs/API.md`.
15///
16/// Matching a name is a chain of byte comparisons the compiler turns into a
17/// length switch: no hash map, no allocation beyond what the field types
18/// own.
19///
20/// # Examples
21///
22/// ```
23/// use form_net::FromForm;
24///
25/// form_net::form! {
26///     /// The sign-up form.
27///     #[derive(Debug)]
28///     pub struct Signup {
29///         pub email: String,
30///         pub password: form_net::Secret,
31///         pub display_name: Option<String> = "display-name",
32///         pub age: u8,
33///         pub newsletter: bool,
34///         pub tags: Vec<String> = "tags[]",
35///     }
36///     ignore = ["csrf_token", "_charset_"];
37/// }
38///
39/// let s = Signup::from_urlencoded(b"email=a%40b.example&password=pw&age=30&tags[]=x&tags[]=y&csrf_token=t")?;
40/// assert_eq!((s.age, s.newsletter, s.tags.len()), (30, false, 2));
41/// assert!(s.display_name.is_none());
42///
43/// let err = Signup::from_urlencoded(b"email=a&password=p&age=300").unwrap_err();
44/// assert_eq!(err.code(), "extract.invalid");
45/// assert!(Signup::from_urlencoded(b"email=a&password=p&age=1&admin=1").is_err());
46/// # Ok::<(), form_net::FormError>(())
47/// ```
48#[macro_export]
49macro_rules! form {
50    (
51        $(#[$meta:meta])*
52        $vis:vis struct $name:ident {
53            $(
54                $(#[$fmeta:meta])*
55                $fvis:vis $field:ident : $ty:ty $(= $wire:literal)?
56            ),* $(,)?
57        }
58        $( ignore = [ $($ignored:literal),* $(,)? ]; )?
59    ) => {
60        $(#[$meta])*
61        $vis struct $name {
62            $(
63                $(#[$fmeta])*
64                $fvis $field: $ty,
65            )*
66        }
67
68        const _: () = {
69            // The struct's own visibility: a builder more public than its
70            // `Output` would put a private type in a public interface.
71            #[doc(hidden)]
72            #[derive(Default)]
73            $vis struct __FormNetBuilder {
74                $( $field: <$ty as $crate::FromField>::Slot, )*
75            }
76
77            impl $crate::FormBuilder for __FormNetBuilder {
78                type Output = $name;
79
80                fn text(&mut self, name: &str, value: &str) -> ::core::result::Result<(), $crate::FormError> {
81                    $(
82                        if name == $crate::__form_wire!($field $(, $wire)?) {
83                            return <$ty as $crate::FromField>::accept_text(&mut self.$field, value)
84                                .map_err(|e| $crate::__value_error(e, ::core::stringify!($field)));
85                        }
86                    )*
87                    $( $( if name == $ignored { return ::core::result::Result::Ok(()); } )* )?
88                    ::core::result::Result::Err($crate::__unknown())
89                }
90
91                fn file(&mut self, name: &str, file: $crate::FileValue<'_>) -> ::core::result::Result<(), $crate::FormError> {
92                    $(
93                        if name == $crate::__form_wire!($field $(, $wire)?) {
94                            return <$ty as $crate::FromField>::accept_file(&mut self.$field, file)
95                                .map_err(|e| $crate::__value_error(e, ::core::stringify!($field)));
96                        }
97                    )*
98                    $( $( if name == $ignored { return ::core::result::Result::Ok(()); } )* )?
99                    let _ = file;
100                    ::core::result::Result::Err($crate::__unknown())
101                }
102
103                fn finish(self) -> ::core::result::Result<$name, $crate::FormError> {
104                    ::core::result::Result::Ok($name {
105                        $(
106                            $field: <$ty as $crate::FromField>::finish(self.$field)
107                                .map_err(|e| $crate::__value_error(e, ::core::stringify!($field)))?,
108                        )*
109                    })
110                }
111
112                fn file_field(&self, name: &str) -> ::core::option::Option<bool> {
113                    $( if name == $crate::__form_wire!($field $(, $wire)?) { return ::core::option::Option::Some(true); } )*
114                    $( $( if name == $ignored { return ::core::option::Option::Some(false); } )* )?
115                    ::core::option::Option::None
116                }
117
118                fn finish_all(
119                    self,
120                    __form_net_errors: &mut $crate::__Vec<$crate::FormError>,
121                ) -> ::core::option::Option<$name> {
122                    let __form_net_before = __form_net_errors.len();
123                    $(
124                        let $field = match <$ty as $crate::FromField>::finish(self.$field) {
125                            ::core::result::Result::Ok(v) => ::core::option::Option::Some(v),
126                            ::core::result::Result::Err(e) => {
127                                $crate::__push_field_error(__form_net_errors, e, ::core::stringify!($field));
128                                ::core::option::Option::None
129                            }
130                        };
131                    )*
132                    if __form_net_errors.len() > __form_net_before {
133                        return ::core::option::Option::None;
134                    }
135                    ::core::option::Option::Some($name { $( $field: $field?, )* })
136                }
137            }
138
139            impl $crate::FromForm for $name {
140                type Builder = __FormNetBuilder;
141            }
142        };
143    };
144}
145
146/// The wire name of a `form!` field. Not part of the API.
147#[doc(hidden)]
148#[macro_export]
149macro_rules! __form_wire {
150    ($field:ident) => {
151        ::core::stringify!($field)
152    };
153    ($field:ident, $wire:literal) => {
154        $wire
155    };
156}