headers/common/
content_type.rs

1use std::fmt;
2
3use mime::{self, Mime};
4
5/// `Content-Type` header, defined in
6/// [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)
7///
8/// The `Content-Type` header field indicates the media type of the
9/// associated representation: either the representation enclosed in the
10/// message payload or the selected representation, as determined by the
11/// message semantics.  The indicated media type defines both the data
12/// format and how that data is intended to be processed by a recipient,
13/// within the scope of the received message semantics, after any content
14/// codings indicated by Content-Encoding are decoded.
15///
16/// Although the `mime` crate allows the mime options to be any slice, this crate
17/// forces the use of Vec. This is to make sure the same header can't have more than 1 type. If
18/// this is an issue, it's possible to implement `Header` on a custom struct.
19///
20/// # ABNF
21///
22/// ```text
23/// Content-Type = media-type
24/// ```
25///
26/// # Example values
27///
28/// * `text/html; charset=utf-8`
29/// * `application/json`
30///
31/// # Examples
32///
33/// ```
34/// # extern crate headers;
35/// use headers::ContentType;
36///
37/// let ct = ContentType::json();
38/// ```
39#[derive(Clone, Debug, PartialEq)]
40pub struct ContentType(Mime);
41
42impl ContentType {
43    /// A constructor  to easily create a `Content-Type: application/json` header.
44    #[inline]
45    pub fn json() -> ContentType {
46        ContentType(mime::APPLICATION_JSON)
47    }
48
49    /// A constructor  to easily create a `Content-Type: text/plain` header.
50    #[inline]
51    pub fn text() -> ContentType {
52        ContentType(mime::TEXT_PLAIN)
53    }
54
55    /// A constructor  to easily create a `Content-Type: text/plain; charset=utf-8` header.
56    #[inline]
57    pub fn text_utf8() -> ContentType {
58        ContentType(mime::TEXT_PLAIN_UTF_8)
59    }
60
61    /// A constructor  to easily create a `Content-Type: text/html` header.
62    #[inline]
63    pub fn html() -> ContentType {
64        ContentType(mime::TEXT_HTML)
65    }
66
67    /// A constructor  to easily create a `Content-Type: text/xml` header.
68    #[inline]
69    pub fn xml() -> ContentType {
70        ContentType(mime::TEXT_XML)
71    }
72
73    /// A constructor  to easily create a `Content-Type: application/www-form-url-encoded` header.
74    #[inline]
75    pub fn form_url_encoded() -> ContentType {
76        ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)
77    }
78    /// A constructor  to easily create a `Content-Type: image/jpeg` header.
79    #[inline]
80    pub fn jpeg() -> ContentType {
81        ContentType(mime::IMAGE_JPEG)
82    }
83
84    /// A constructor  to easily create a `Content-Type: image/png` header.
85    #[inline]
86    pub fn png() -> ContentType {
87        ContentType(mime::IMAGE_PNG)
88    }
89
90    /// A constructor  to easily create a `Content-Type: application/octet-stream` header.
91    #[inline]
92    pub fn octet_stream() -> ContentType {
93        ContentType(mime::APPLICATION_OCTET_STREAM)
94    }
95}
96
97impl ::Header for ContentType {
98    fn name() -> &'static ::HeaderName {
99        &::http::header::CONTENT_TYPE
100    }
101
102    fn decode<'i, I: Iterator<Item = &'i ::HeaderValue>>(values: &mut I) -> Result<Self, ::Error> {
103        values
104            .next()
105            .and_then(|v| v.to_str().ok()?.parse().ok())
106            .map(ContentType)
107            .ok_or_else(::Error::invalid)
108    }
109
110    fn encode<E: Extend<::HeaderValue>>(&self, values: &mut E) {
111        let value = self
112            .0
113            .as_ref()
114            .parse()
115            .expect("Mime is always a valid HeaderValue");
116        values.extend(::std::iter::once(value));
117    }
118}
119
120impl From<mime::Mime> for ContentType {
121    fn from(m: mime::Mime) -> ContentType {
122        ContentType(m)
123    }
124}
125
126impl From<ContentType> for mime::Mime {
127    fn from(ct: ContentType) -> mime::Mime {
128        ct.0
129    }
130}
131
132impl fmt::Display for ContentType {
133    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134        fmt::Display::fmt(&self.0, f)
135    }
136}
137
138impl std::str::FromStr for ContentType {
139    type Err = ::Error;
140
141    fn from_str(s: &str) -> Result<ContentType, Self::Err> {
142        s.parse::<Mime>()
143            .map(|m| m.into())
144            .map_err(|_| ::Error::invalid())
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::super::test_decode;
151    use super::ContentType;
152
153    #[test]
154    fn json() {
155        assert_eq!(
156            test_decode::<ContentType>(&["application/json"]),
157            Some(ContentType::json()),
158        );
159    }
160
161    #[test]
162    fn from_str() {
163        assert_eq!(
164            "application/json".parse::<ContentType>().unwrap(),
165            ContentType::json(),
166        );
167        assert!("invalid-mimetype".parse::<ContentType>().is_err());
168    }
169
170    bench_header!(bench_plain, ContentType, "text/plain");
171    bench_header!(bench_json, ContentType, "application/json");
172    bench_header!(
173        bench_formdata,
174        ContentType,
175        "multipart/form-data; boundary=---------------abcd"
176    );
177}