35
35
mod body;
36
36
mod client;
37
37
mod header;
38
+ #[ cfg( feature = "multipart" ) ]
39
+ #[ cfg_attr( docsrs, doc( cfg( feature = "multipart" ) ) ) ]
40
+ pub mod multipart;
38
41
mod request;
39
42
mod response;
40
43
@@ -73,7 +76,9 @@ pub use self::{
73
76
pub use waki_macros:: handler;
74
77
75
78
use crate :: body:: Body ;
76
- use anyhow:: Result ;
79
+ #[ cfg( feature = "multipart" ) ]
80
+ use crate :: multipart:: { parser:: parse, Form , Part } ;
81
+ use anyhow:: { anyhow, Result } ;
77
82
use serde:: Serialize ;
78
83
use std:: collections:: HashMap ;
79
84
@@ -129,6 +134,33 @@ macro_rules! impl_common_get_methods {
129
134
pub fn json<T : serde:: de:: DeserializeOwned >( self ) -> Result <T > {
130
135
Ok ( serde_json:: from_slice( self . body( ) ?. as_ref( ) ) ?)
131
136
}
137
+
138
+ /// Parse the body as multipart/form-data.
139
+ ///
140
+ /// # Optional
141
+ ///
142
+ /// This requires the `multipart` feature enabled.
143
+ #[ cfg( feature = "multipart" ) ]
144
+ #[ cfg_attr( docsrs, doc( cfg( feature = "multipart" ) ) ) ]
145
+ pub fn multipart( self ) -> Result <HashMap <String , Part >> {
146
+ match self . headers. get( "Content-Type" ) {
147
+ Some ( header) => {
148
+ let mime = header. parse:: <mime:: Mime >( ) ?;
149
+ let boundary = match mime. get_param( mime:: BOUNDARY ) {
150
+ Some ( v) => v. as_str( ) ,
151
+ None => {
152
+ return Err ( anyhow!(
153
+ "unable to find the boundary value in the Content-Type header"
154
+ ) )
155
+ }
156
+ } ;
157
+ parse( self . body( ) ?. as_ref( ) , boundary)
158
+ }
159
+ None => Err ( anyhow!(
160
+ "parse body as multipart failed, unable to find the Content-Type header"
161
+ ) ) ,
162
+ }
163
+ }
132
164
}
133
165
) +)
134
166
}
@@ -141,12 +173,10 @@ macro_rules! impl_common_set_methods {
141
173
/// Add a header.
142
174
///
143
175
/// ```
144
- /// # use anyhow::Result;
145
176
/// # use waki::ResponseBuilder;
146
- /// # fn run() -> Result<()> {
177
+ /// # fn run() {
147
178
/// # let r = ResponseBuilder::new();
148
179
/// r.header("Content-Type", "application/json");
149
- /// # Ok(())
150
180
/// # }
151
181
/// ```
152
182
pub fn header<S : Into <String >>( mut self , key: S , value: S ) -> Self {
@@ -159,12 +189,10 @@ macro_rules! impl_common_set_methods {
159
189
/// Add a set of headers.
160
190
///
161
191
/// ```
162
- /// # use anyhow::Result;
163
192
/// # use waki::ResponseBuilder;
164
- /// # fn run() -> Result<()> {
193
+ /// # fn run() {
165
194
/// # let r = ResponseBuilder::new();
166
195
/// r.headers([("Content-Type", "application/json"), ("Accept", "*/*")]);
167
- /// # Ok(())
168
196
/// # }
169
197
/// ```
170
198
pub fn headers<S , I >( mut self , headers: I ) -> Self
@@ -182,15 +210,13 @@ macro_rules! impl_common_set_methods {
182
210
/// Set the body.
183
211
///
184
212
/// ```
185
- /// # use anyhow::Result;
186
213
/// # use waki::ResponseBuilder;
187
- /// # fn run() -> Result<()> {
214
+ /// # fn run() {
188
215
/// # let r = ResponseBuilder::new();
189
- /// r.body("hello".as_bytes());
190
- /// # Ok(())
216
+ /// r.body("hello");
191
217
/// # }
192
218
/// ```
193
- pub fn body( mut self , body: & [ u8 ] ) -> Self {
219
+ pub fn body< V : Into < Vec < u8 >>> ( mut self , body: V ) -> Self {
194
220
if let Ok ( ref mut inner) = self . inner {
195
221
inner. body = Body :: Bytes ( body. into( ) ) ;
196
222
}
@@ -204,13 +230,11 @@ macro_rules! impl_common_set_methods {
204
230
/// This requires the `json` feature enabled.
205
231
///
206
232
/// ```
207
- /// # use anyhow::Result;
208
233
/// # use std::collections::HashMap;
209
234
/// # use waki::ResponseBuilder;
210
- /// # fn run() -> Result<()> {
235
+ /// # fn run() {
211
236
/// # let r = ResponseBuilder::new();
212
237
/// r.json(&HashMap::from([("data", "hello")]));
213
- /// # Ok(())
214
238
/// # }
215
239
/// ```
216
240
#[ cfg( feature = "json" ) ]
@@ -234,12 +258,10 @@ macro_rules! impl_common_set_methods {
234
258
/// Set a form body.
235
259
///
236
260
/// ```
237
- /// # use anyhow::Result;
238
261
/// # use waki::ResponseBuilder;
239
- /// # fn run() -> Result<()> {
262
+ /// # fn run() {
240
263
/// # let r = ResponseBuilder::new();
241
264
/// r.form(&[("a", "b"), ("c", "d")]);
242
- /// # Ok(())
243
265
/// # }
244
266
/// ```
245
267
pub fn form<T : Serialize + ?Sized >( mut self , form: & T ) -> Self {
@@ -259,6 +281,46 @@ macro_rules! impl_common_set_methods {
259
281
}
260
282
self
261
283
}
284
+
285
+ /// Set a multipart/form-data body.
286
+ ///
287
+ /// # Optional
288
+ ///
289
+ /// This requires the `multipart` feature enabled.
290
+ ///
291
+ /// ```
292
+ /// # use anyhow::Result;
293
+ /// # use waki::ResponseBuilder;
294
+ /// # fn run() -> Result<()> {
295
+ /// # let r = ResponseBuilder::new();
296
+ /// let form = waki::multipart::Form::new()
297
+ /// // Add a text field
298
+ /// .text("key", "value")
299
+ /// // And a file
300
+ /// .file("file", "/path/to/file.txt")?
301
+ /// // And a custom part
302
+ /// .part(
303
+ /// waki::multipart::Part::new("key2", "value2")
304
+ /// .filename("file.txt")
305
+ /// .mime_str("text/plain")?,
306
+ /// );
307
+ ///
308
+ /// r.multipart(form);
309
+ /// # Ok(())
310
+ /// # }
311
+ /// ```
312
+ #[ cfg( feature = "multipart" ) ]
313
+ #[ cfg_attr( docsrs, doc( cfg( feature = "multipart" ) ) ) ]
314
+ pub fn multipart( mut self , form: Form ) -> Self {
315
+ if let Ok ( ref mut inner) = self . inner {
316
+ inner. headers. insert(
317
+ "Content-Type" . into( ) ,
318
+ format!( "multipart/form-data; boundary={}" , form. boundary( ) ) ,
319
+ ) ;
320
+ inner. body = Body :: Bytes ( form. build( ) ) ;
321
+ }
322
+ self
323
+ }
262
324
}
263
325
) +)
264
326
}
0 commit comments