multer_derive/
from_multipart.rs1use 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#[derive(Default, Clone, Debug)]
12pub struct FormContext<'a> {
13 pub field_name: Option<&'a str>,
15}
16
17pub trait FromMultipart: Sized {
19 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}