Skip to main content

formdata/
form_data.rs

1// Copyright © 2015 by Michael Dilger (of New Zealand)
2// This code is licensed under the MIT license (see LICENSE-MIT for details)
3
4use mime_multipart::{Node, Part, FilePart};
5use hyper::header::{Headers, ContentDisposition, DispositionParam, DispositionType,
6                    ContentType};
7use mime::{Mime, TopLevel, SubLevel};
8use error::Error;
9
10/// The extracted text fields and uploaded files from a `multipart/form-data` request.
11///
12/// Use `parse_multipart` to devise this object from a request.
13#[derive(Clone, Debug, PartialEq)]
14pub struct FormData {
15    /// Name-value pairs for plain text fields. Technically, these are form data parts with no
16    /// filename specified in the part's `Content-Disposition`.
17    pub fields: Vec<(String, String)>,
18    /// Name-value pairs for temporary files. Technically, these are form data parts with a filename
19    /// specified in the part's `Content-Disposition`.
20    pub files: Vec<(String, FilePart)>,
21}
22
23impl FormData {
24    pub fn new() -> FormData {
25        FormData { fields: vec![], files: vec![] }
26    }
27
28    /// Create a mime-multipart Vec<Node> from this FormData
29    pub fn to_multipart(&self) -> Result<Vec<Node>, Error> {
30        // Translate to Nodes
31        let mut nodes: Vec<Node> = Vec::with_capacity(self.fields.len() + self.files.len());
32
33        for &(ref name, ref value) in &self.fields {
34            let mut h = Headers::new();
35            h.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
36            h.set(ContentDisposition {
37                disposition: DispositionType::Ext("form-data".to_owned()),
38                parameters: vec![DispositionParam::Ext("name".to_owned(), name.clone())],
39            });
40            nodes.push( Node::Part( Part {
41                headers: h,
42                body: value.as_bytes().to_owned(),
43            }));
44        }
45
46        for &(ref name, ref filepart) in &self.files {
47            let mut filepart = filepart.clone();
48            // We leave all headers that the caller specified, except that we rewrite
49            // Content-Disposition.
50            while filepart.headers.remove::<ContentDisposition>() { };
51            let filename = match filepart.path.file_name() {
52                Some(fname) => fname.to_string_lossy().into_owned(),
53                None => return Err(Error::NotAFile),
54            };
55            filepart.headers.set(ContentDisposition {
56                disposition: DispositionType::Ext("form-data".to_owned()),
57                parameters: vec![DispositionParam::Ext("name".to_owned(), name.clone()),
58                                 DispositionParam::Ext("filename".to_owned(), filename)],
59            });
60            nodes.push( Node::File( filepart ) );
61        }
62
63        Ok(nodes)
64    }
65}