multer_derive/
from_multipart.rs

1use crate::{
2    error::Error, from_multipart_field::FromMultipartField, multipart_form::MultipartForm,
3};
4use std::{
5    collections::{BTreeMap, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},
6    hash::Hash,
7    str::FromStr,
8};
9
10/// Additional information for parsing a multipart form.
11#[derive(Default, Clone, Debug)]
12pub struct FormContext<'a> {
13    /// The name of the field being parsed, if any.
14    pub field_name: Option<&'a str>,
15}
16
17/// Allows to create a type from a [`multer::Multipart`].
18pub trait FromMultipart: Sized {
19    /// Constructs this type from the given multipart form.
20    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error>;
21}
22
23impl<T: FromMultipartField> FromMultipart for T {
24    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error> {
25        let Some(field_name) = ctx.field_name else {
26            return Err(Error::new("FormContext does not specified a field to parse"));
27        };
28
29        let field = multipart
30            .get_by_name(field_name)
31            .ok_or_else(|| Error::new(format!("`{field_name}` form field was not found")))?;
32
33        T::from_field(field)
34    }
35}
36
37impl<K, V> FromMultipart for HashMap<K, V>
38where
39    K: FromStr + Hash + Eq + Send,
40    V: FromMultipartField + Send,
41    K::Err: std::error::Error + Send + Sync + 'static,
42{
43    fn from_multipart(multipart: &MultipartForm, _ctx: FormContext<'_>) -> Result<Self, Error> {
44        let mut map = HashMap::new();
45
46        for field in multipart.fields() {
47            let Some(name) = field.name() else {
48                continue;
49            };
50
51            let key = K::from_str(name).map_err(Error::new)?;
52            let value = V::from_field(field)?;
53            map.insert(key, value);
54        }
55
56        Ok(map)
57    }
58}
59
60impl<K, V> FromMultipart for BTreeMap<K, V>
61where
62    K: FromStr + Ord + Send,
63    V: FromMultipartField + Send,
64    K::Err: std::error::Error + Send + Sync + 'static,
65{
66    fn from_multipart(multipart: &MultipartForm, _ctx: FormContext<'_>) -> Result<Self, Error> {
67        let mut map = BTreeMap::new();
68
69        for field in multipart.fields() {
70            let Some(name) = field.name() else {
71                continue;
72            };
73
74            let key = K::from_str(name).map_err(Error::new)?;
75            let value = V::from_field(field)?;
76            map.insert(key, value);
77        }
78
79        Ok(map)
80    }
81}
82
83impl<T: FromMultipartField> FromMultipart for Vec<T> {
84    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error> {
85        let name = ctx.field_name;
86
87        Ok(multipart
88            .fields()
89            .iter()
90            .filter(|f| name.is_none() || name == f.name())
91            .filter_map(|f| T::from_field(f).ok())
92            .collect())
93    }
94}
95
96impl<T: FromMultipartField> FromMultipart for VecDeque<T> {
97    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error> {
98        let name = ctx.field_name;
99
100        Ok(multipart
101            .fields()
102            .iter()
103            .filter(|f| name.is_none() || name == f.name())
104            .filter_map(|f| T::from_field(f).ok())
105            .collect())
106    }
107}
108
109impl<T: FromMultipartField> FromMultipart for LinkedList<T> {
110    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error> {
111        let name = ctx.field_name;
112
113        Ok(multipart
114            .fields()
115            .iter()
116            .filter(|f| name.is_none() || name == f.name())
117            .filter_map(|f| T::from_field(f).ok())
118            .collect())
119    }
120}
121
122impl<T: FromMultipartField + Hash + Eq> FromMultipart for HashSet<T> {
123    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error> {
124        let name = ctx.field_name;
125
126        Ok(multipart
127            .fields()
128            .iter()
129            .filter(|f| name.is_none() || name == f.name())
130            .filter_map(|f| T::from_field(f).ok())
131            .collect())
132    }
133}
134
135impl<T: FromMultipartField + Ord> FromMultipart for BinaryHeap<T> {
136    fn from_multipart(multipart: &MultipartForm, ctx: FormContext<'_>) -> Result<Self, Error> {
137        let name = ctx.field_name;
138
139        Ok(multipart
140            .fields()
141            .iter()
142            .filter(|f| name.is_none() || name == f.name())
143            .filter_map(|f| T::from_field(f).ok())
144            .collect())
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use multer::Multipart;
151
152    use crate::{FormFile, FromMultipart, MultipartForm};
153
154    use super::FormContext;
155
156    struct Person {
157        name: String,
158        email: String,
159        age: u8,
160        married: bool,
161        photo: FormFile,
162    }
163
164    impl FromMultipart for Person {
165        fn from_multipart(
166            multipart: &crate::MultipartForm,
167            _ctx: FormContext<'_>,
168        ) -> Result<Self, crate::Error> {
169            let name = <String as crate::FromMultipart>::from_multipart(
170                multipart,
171                FormContext {
172                    field_name: Some("name"),
173                },
174            )?;
175
176            let email = <String as crate::FromMultipart>::from_multipart(
177                multipart,
178                FormContext {
179                    field_name: Some("email"),
180                },
181            )?;
182
183            let age = <u8 as crate::FromMultipart>::from_multipart(
184                multipart,
185                FormContext {
186                    field_name: Some("age"),
187                },
188            )?;
189
190            let married = <bool as crate::FromMultipart>::from_multipart(
191                multipart,
192                FormContext {
193                    field_name: Some("married"),
194                },
195            )?;
196
197            let photo = <FormFile as crate::FromMultipart>::from_multipart(
198                multipart,
199                FormContext {
200                    field_name: Some("photo"),
201                },
202            )?;
203
204            Ok(Self {
205                name,
206                email,
207                age,
208                married,
209                photo,
210            })
211        }
212    }
213
214    #[tokio::test]
215    async fn parse_struct_test() {
216        const FORM_DATA : &str = "--boundary_string\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nJohn Smith\r\n--boundary_string\r\nContent-Disposition: form-data; name=\"email\"\r\n\r\njohn@example.com\r\n--boundary_string\r\nContent-Disposition: form-data; name=\"age\"\r\n\r\n25\r\n--boundary_string\r\nContent-Disposition: form-data; name=\"married\"\r\n\r\ntrue\r\n--boundary_string\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"example.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n[Binary data]\r\n--boundary_string--\r\n";
217
218        let reader = FORM_DATA.as_bytes();
219        let multipart = Multipart::with_reader(reader, "boundary_string");
220
221        let form = MultipartForm::with_multipart(multipart).await.unwrap();
222        let person = Person::from_multipart(&form, Default::default()).unwrap();
223
224        assert_eq!(person.name, "John Smith");
225        assert_eq!(person.email, "john@example.com");
226        assert_eq!(person.age, 25);
227        assert_eq!(person.married, true);
228
229        let str = String::from_utf8(person.photo.bytes().to_vec()).unwrap();
230        assert_eq!(str, "[Binary data]");
231    }
232}