axum_test/multipart/
part.rs1use crate::internals::ErrorMessage;
2use bytes::Bytes;
3use http::HeaderName;
4use http::HeaderValue;
5use mime::Mime;
6use std::fmt::Debug;
7use std::fmt::Display;
8
9#[derive(Debug, Clone)]
16pub struct Part {
17 pub(crate) bytes: Bytes,
18 pub(crate) file_name: Option<String>,
19 pub(crate) mime_type: Mime,
20 pub(crate) headers: Vec<(HeaderName, HeaderValue)>,
21}
22
23impl Part {
24 pub fn text<T>(text: T) -> Self
28 where
29 T: Display,
30 {
31 let bytes = text.to_string().into_bytes().into();
32
33 Self::new(bytes, mime::TEXT_PLAIN)
34 }
35
36 pub fn bytes<B>(bytes: B) -> Self
40 where
41 B: Into<Bytes>,
42 {
43 Self::new(bytes.into(), mime::APPLICATION_OCTET_STREAM)
44 }
45
46 fn new(bytes: Bytes, mime_type: Mime) -> Self {
47 Self {
48 bytes,
49 file_name: None,
50 mime_type,
51 headers: Default::default(),
52 }
53 }
54
55 pub fn file_name<T>(mut self, file_name: T) -> Self
59 where
60 T: Display,
61 {
62 self.file_name = Some(file_name.to_string());
63 self
64 }
65
66 pub fn mime_type<M>(mut self, mime_type: M) -> Self
72 where
73 M: AsRef<str>,
74 {
75 let raw_mime_type = mime_type.as_ref();
76 let parsed_mime_type = raw_mime_type
77 .parse()
78 .error_message_fn(|| format!("Failed to parse '{raw_mime_type}' as a Mime type"));
79
80 self.mime_type = parsed_mime_type;
81
82 self
83 }
84
85 pub fn add_header<N, V>(mut self, name: N, value: V) -> Self
115 where
116 N: TryInto<HeaderName>,
117 N::Error: Debug,
118 V: TryInto<HeaderValue>,
119 V::Error: Debug,
120 {
121 let header_name: HeaderName = name
122 .try_into()
123 .expect("Failed to convert header name to HeaderName");
124 let header_value: HeaderValue = value
125 .try_into()
126 .expect("Failed to convert header vlue to HeaderValue");
127
128 self.headers.push((header_name, header_value));
129 self
130 }
131}
132
133#[cfg(test)]
134mod test_text {
135 use super::*;
136
137 #[test]
138 fn it_should_contain_text_given() {
139 let part = Part::text("some_text");
140
141 let output = String::from_utf8_lossy(&part.bytes);
142 assert_eq!(output, "some_text");
143 }
144
145 #[test]
146 fn it_should_use_mime_type_text() {
147 let part = Part::text("some_text");
148 assert_eq!(part.mime_type, mime::TEXT_PLAIN);
149 }
150}
151
152#[cfg(test)]
153mod test_byes {
154 use super::*;
155
156 #[test]
157 fn it_should_contain_bytes_given() {
158 let bytes = "some_text".as_bytes();
159 let part = Part::bytes(bytes);
160
161 let output = String::from_utf8_lossy(&part.bytes);
162 assert_eq!(output, "some_text");
163 }
164
165 #[test]
166 fn it_should_use_mime_type_octet_stream() {
167 let bytes = "some_text".as_bytes();
168 let part = Part::bytes(bytes);
169
170 assert_eq!(part.mime_type, mime::APPLICATION_OCTET_STREAM);
171 }
172}
173
174#[cfg(test)]
175mod test_file_name {
176 use super::*;
177
178 #[test]
179 fn it_should_use_file_name_given() {
180 let mut part = Part::text("some_text");
181
182 assert_eq!(part.file_name, None);
183 part = part.file_name("my-text.txt");
184 assert_eq!(part.file_name, Some("my-text.txt".to_string()));
185 }
186}
187
188#[cfg(test)]
189mod test_mime_type {
190 use super::*;
191 use crate::testing::catch_panic_error_message;
192 use pretty_assertions::assert_str_eq;
193
194 #[test]
195 fn it_should_use_mime_type_set() {
196 let mut part = Part::text("some_text");
197
198 assert_eq!(part.mime_type, mime::TEXT_PLAIN);
199 part = part.mime_type("application/json");
200 assert_eq!(part.mime_type, mime::APPLICATION_JSON);
201 }
202
203 #[test]
204 fn it_should_error_if_invalid_mime_type() {
205 let part = Part::text("some_text");
206
207 let message = catch_panic_error_message(|| {
208 part.mime_type("🦊");
209 });
210 assert_str_eq!(
211 "Failed to parse '🦊' as a Mime type,
212 mime parse error: an invalid token was encountered, F0 at position 0
213",
214 message
215 );
216 }
217}