actix_multipart_test/
lib.rs1use std::path::Path;
2
3use uuid::Uuid;
4
5pub struct MultiPartFormDataBuilder {
45 files: Vec<(String, String, String, Box<dyn AsRef<Path>>)>,
46 texts: Vec<(String, String, String)>,
47}
48
49impl MultiPartFormDataBuilder {
50 pub fn new() -> MultiPartFormDataBuilder {
52 MultiPartFormDataBuilder {
53 files: vec![],
54 texts: vec![],
55 }
56 }
57
58 pub fn with_text(
66 &mut self,
67 name: impl Into<String>,
68 value: impl Into<String>,
69 ) -> &mut MultiPartFormDataBuilder {
70 self.texts
71 .push((name.into(), value.into(), "text/plain".to_string()));
72 self
73 }
74
75 pub fn with_file(
85 &mut self,
86 path: impl AsRef<Path> + 'static,
87 name: impl Into<String>,
88 content_type: impl Into<String>,
89 file_name: impl Into<String>,
90 ) -> &mut MultiPartFormDataBuilder {
91 self.files.push((
92 name.into(),
93 file_name.into(),
94 content_type.into(),
95 Box::new(path),
96 ));
97 self
98 }
99
100 pub fn build(&self) -> ((String, String), Vec<u8>) {
110 let boundary = Uuid::new_v4().to_string();
111
112 let mut body = vec![];
113
114 for file in self.files.iter() {
115 body.extend(format!("--{}\r\n", boundary).as_bytes());
116 body.extend(
117 format!(
118 "Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n",
119 file.0, file.1
120 )
121 .as_bytes(),
122 );
123 body.extend(format!("Content-Type: {}\r\n", file.2).as_bytes());
124 let data = std::fs::read(file.3.as_ref()).unwrap();
125 body.extend(format!("Content-Length: {}\r\n\r\n", data.len()).as_bytes());
126 body.extend(data);
127 body.extend("\r\n".as_bytes());
128 }
129
130 for text in self.texts.iter() {
131 body.extend(format!("--{}\r\n", boundary).as_bytes());
132 body.extend(
133 format!("Content-Disposition: form-data; name=\"{}\"\r\n", text.0).as_bytes(),
134 );
135 body.extend(format!("Content-Type: {}\r\n", text.2).as_bytes());
136 let data = text.1.as_bytes();
137 body.extend(format!("Content-Length: {}\r\n\r\n", data.len()).as_bytes());
138 body.extend(data);
139 body.extend("\r\n".as_bytes());
140 }
141
142 body.extend(format!("--{}--\r\n", boundary).as_bytes());
143
144 let header_value = format!("multipart/form-data; boundary={}", boundary);
145 let header = ("Content-Type".to_string(), header_value);
146
147 (header, body)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_should_build_multipart_form_with_text() {
157 let mut multipart_form_data_builder = MultiPartFormDataBuilder::new();
158 multipart_form_data_builder.with_file(
159 "tests/sample.png",
160 "sample",
161 "image/png",
162 "sample.png",
163 );
164 multipart_form_data_builder.with_text("name", "some_name");
165 let (header, body) = multipart_form_data_builder.build();
166
167 assert_eq!(header.0, "Content-Type");
168 assert!(header.1.starts_with("multipart/form-data; boundary="));
169 assert!(body.len() > 0);
170 }
171}