Skip to content

Commit

Permalink
feat(mime): upgrade to mime v0.3
Browse files Browse the repository at this point in the history
The new mime crate has several benefits:

- Faster formatting
- Easier to use. Most common mime types are now just constants, like
  `mime::TEXT_PLAIN`.
- Proper suffix support.
- Extensible without breaking backwards compatiblity. This means we can
  always add new constants, but before we couldn't add new variants to the
  enums.
- It's now impossible for a `Mime` to contain invalid tokens. Before,
  with the `Ext(String)` variants, it was possible to create an illegal
  mime.

Closes #738

BREAKING CHANGE: Most uses of `mime` will likely break. There is no more
  `mime!` macro, nor a `Mime` constructor, nor `TopLevel` and `SubLevel`
  enums.

  Instead, in most cases, a constant exists that can now be used.

  For less common mime types, they can be created by parsing a string.
  • Loading branch information
seanmonstar committed Jun 8, 2017
1 parent e2ed6f5 commit f273224
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 94 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ futures-cpupool = "0.1"
httparse = "1.0"
language-tags = "0.2"
log = "0.3"
mime = "0.2"
mime = "0.3"
time = "0.1"
tokio-core = "0.1.6"
tokio-proto = "0.1"
Expand Down
75 changes: 35 additions & 40 deletions src/header/common/accept.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use mime::Mime;
use mime::{self, Mime};

use header::{QualityItem, qitem};

Expand All @@ -24,94 +24,89 @@ header! {
/// ```
///
/// # Example values
/// * `audio/*; q=0.2, audio/basic` (`*` value won't parse correctly)
/// * `audio/*; q=0.2, audio/basic`
/// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`
///
/// # Examples
/// ```
/// use hyper::header::{Headers, Accept, qitem};
/// use hyper::mime::{Mime, TopLevel, SubLevel};
/// use hyper::mime;
///
/// let mut headers = Headers::new();
///
/// headers.set(
/// Accept(vec![
/// qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
/// qitem(mime::TEXT_HTML),
/// ])
/// );
/// ```
/// ```
/// use hyper::header::{Headers, Accept, qitem};
/// use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
/// use hyper::mime;
///
/// let mut headers = Headers::new();
/// headers.set(
/// Accept(vec![
/// qitem(Mime(TopLevel::Application, SubLevel::Json,
/// vec![(Attr::Charset, Value::Utf8)])),
/// qitem(mime::APPLICATION_JSON),
/// ])
/// );
/// ```
/// ```
/// use hyper::header::{Headers, Accept, QualityItem, q, qitem};
/// use hyper::mime::{Mime, TopLevel, SubLevel};
/// use hyper::mime;
///
/// let mut headers = Headers::new();
///
/// headers.set(
/// Accept(vec![
/// qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
/// qitem(Mime(TopLevel::Application,
/// SubLevel::Ext("xhtml+xml".to_owned()), vec![])),
/// QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]),
/// q(900)),
/// qitem(Mime(TopLevel::Image,
/// SubLevel::Ext("webp".to_owned()), vec![])),
/// QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]),
/// q(800))
/// qitem(mime::TEXT_HTML),
/// qitem("application/xhtml+xml".parse().unwrap()),
/// QualityItem::new(
/// mime::TEXT_XML,
/// q(900)
/// ),
/// qitem("image/webp".parse().unwrap()),
/// QualityItem::new(
/// mime::STAR_STAR,
/// q(800)
/// ),
/// ])
/// );
/// ```
///
/// # Notes
/// * Using always Mime types to represent `media-range` differs from the ABNF.
/// * **FIXME**: `accept-ext` is not supported.
(Accept, "Accept") => (QualityItem<Mime>)+

test_accept {
// Tests from the RFC
// FIXME: Test fails, first value containing a "*" fails to parse
// test_header!(
// test1,
// vec![b"audio/*; q=0.2, audio/basic"],
// Some(HeaderField(vec![
// QualityItem::new(Mime(TopLevel::Audio, SubLevel::Star, vec![]), q(200)),
// qitem(Mime(TopLevel::Audio, SubLevel::Ext("basic".to_owned()), vec![])),
// ])));
test_header!(
test1,
vec![b"audio/*; q=0.2, audio/basic"],
Some(HeaderField(vec![
QualityItem::new("audio/*".parse().unwrap(), q(200)),
qitem("audio/basic".parse().unwrap()),
])));
test_header!(
test2,
vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"],
Some(HeaderField(vec![
QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![]), q(500)),
qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
QualityItem::new(TEXT_PLAIN, q(500)),
qitem(TEXT_HTML),
QualityItem::new(
Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_owned()), vec![]),
"text/x-dvi".parse().unwrap(),
q(800)),
qitem(Mime(TopLevel::Text, SubLevel::Ext("x-c".to_owned()), vec![])),
qitem("text/x-c".parse().unwrap()),
])));
// Custom tests
test_header!(
test3,
vec![b"text/plain; charset=utf-8"],
Some(Accept(vec![
qitem(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])),
qitem(TEXT_PLAIN_UTF_8),
])));
test_header!(
test4,
vec![b"text/plain; charset=utf-8; q=0.5"],
Some(Accept(vec![
QualityItem::new(Mime(TopLevel::Text,
SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]),
QualityItem::new(TEXT_PLAIN_UTF_8,
q(500)),
])));

Expand All @@ -127,22 +122,22 @@ header! {
impl Accept {
/// A constructor to easily create `Accept: */*`.
pub fn star() -> Accept {
Accept(vec![qitem(mime!(Star/Star))])
Accept(vec![qitem(mime::STAR_STAR)])
}

/// A constructor to easily create `Accept: application/json`.
pub fn json() -> Accept {
Accept(vec![qitem(mime!(Application/Json))])
Accept(vec![qitem(mime::APPLICATION_JSON)])
}

/// A constructor to easily create `Accept: text/*`.
pub fn text() -> Accept {
Accept(vec![qitem(mime!(Text/Star))])
Accept(vec![qitem(mime::TEXT_STAR)])
}

/// A constructor to easily create `Accept: image/*`.
pub fn image() -> Accept {
Accept(vec![qitem(mime!(Image/Star))])
Accept(vec![qitem(mime::IMAGE_STAR)])
}
}

Expand Down
35 changes: 15 additions & 20 deletions src/header/common/content_type.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use mime::Mime;
use mime::{self, Mime};

header! {
/// `Content-Type` header, defined in
Expand All @@ -22,7 +22,8 @@ header! {
/// ```
///
/// # Example values
/// * `text/html; charset=ISO-8859-4`
/// * `text/html; charset=utf-8`
/// * `application/json
///
/// # Examples
/// ```
Expand All @@ -36,73 +37,67 @@ header! {
/// ```
/// ```
/// use hyper::header::{Headers, ContentType};
/// use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
/// use hyper::mime;
///
/// let mut headers = Headers::new();
///
/// headers.set(
/// ContentType(Mime(TopLevel::Text, SubLevel::Html,
/// vec![(Attr::Charset, Value::Utf8)]))
/// ContentType(mime::TEXT_HTML)
/// );
/// ```
(ContentType, "Content-Type") => [Mime]

test_content_type {
test_header!(
test1,
// FIXME: Should be b"text/html; charset=ISO-8859-4" but mime crate lowercases
// the whole value so parsing and formatting the value gives a different result
vec![b"text/html; charset=iso-8859-4"],
Some(HeaderField(Mime(
TopLevel::Text,
SubLevel::Html,
vec![(Attr::Charset, Value::Ext("iso-8859-4".to_owned()))]))));
vec![b"text/html"],
Some(HeaderField(TEXT_HTML)));
}
}

impl ContentType {
/// A constructor to easily create a `Content-Type: application/json` header.
#[inline]
pub fn json() -> ContentType {
ContentType(mime!(Application/Json))
ContentType(mime::APPLICATION_JSON)
}

/// A constructor to easily create a `Content-Type: text/plain; charset=utf-8` header.
#[inline]
pub fn plaintext() -> ContentType {
ContentType(mime!(Text/Plain; Charset=Utf8))
ContentType(mime::TEXT_PLAIN_UTF_8)
}

/// A constructor to easily create a `Content-Type: text/html; charset=utf-8` header.
#[inline]
pub fn html() -> ContentType {
ContentType(mime!(Text/Html; Charset=Utf8))
ContentType(mime::TEXT_HTML)
}

/// A constructor to easily create a `Content-Type: application/www-form-url-encoded` header.
#[inline]
pub fn form_url_encoded() -> ContentType {
ContentType(mime!(Application/WwwFormUrlEncoded))
ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)
}
/// A constructor to easily create a `Content-Type: image/jpeg` header.
#[inline]
pub fn jpeg() -> ContentType {
ContentType(mime!(Image/Jpeg))
ContentType(mime::IMAGE_JPEG)
}

/// A constructor to easily create a `Content-Type: image/png` header.
#[inline]
pub fn png() -> ContentType {
ContentType(mime!(Image/Png))
ContentType(mime::IMAGE_PNG)
}

/// A constructor to easily create a `Content-Type: application/octet-stream` header.
#[inline]
pub fn octet_stream() -> ContentType {
ContentType(mime!(Application/OctetStream))
ContentType(mime::APPLICATION_OCTET_STREAM)
}
}

impl Eq for ContentType {}

bench_header!(bench, ContentType, { vec![b"application/json; charset=utf-8".to_vec()] });
bench_header!(bench, ContentType, { vec![b"application/json".to_vec()] });
8 changes: 3 additions & 5 deletions src/header/common/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,9 +905,7 @@ mod tests {
use http::{ServerTransaction, Http1Transaction};
use bytes::BytesMut;

use mime::Mime;
use mime::TopLevel::Text;
use mime::SubLevel::Plain;
use mime;

#[test]
fn test_link() {
Expand Down Expand Up @@ -956,7 +954,7 @@ mod tests {
.push_media_desc(MediaDesc::Screen)
.set_title("previous chapter")
.set_title_star("title* unparsed")
.set_media_type(Mime(Text, Plain, vec![]));
.set_media_type(mime::TEXT_PLAIN);

let link_header = b"<http://example.com/TheBook/chapter2>; \
rel=\"previous\"; anchor=\"../anchor/example/\"; \
Expand Down Expand Up @@ -1015,7 +1013,7 @@ mod tests {
.push_media_desc(MediaDesc::Screen)
.set_title("previous chapter")
.set_title_star("title* unparsed")
.set_media_type(Mime(Text, Plain, vec![]));
.set_media_type(mime::TEXT_PLAIN);

let link = Link::new(vec![link_value]);

Expand Down
2 changes: 1 addition & 1 deletion src/header/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! ## Mime
//!
//! Several header fields use MIME values for their contents. Keeping with the
//! strongly-typed theme, the [mime](http://seanmonstar.github.io/mime.rs) crate
//! strongly-typed theme, the [mime](https://docs.rs/mime) crate
//! is used, such as `ContentType(pub Mime)`.
pub use self::accept::Accept;
Expand Down
29 changes: 3 additions & 26 deletions src/header/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,11 +696,7 @@ impl PartialEq<HeaderName> for str {
#[cfg(test)]
mod tests {
use std::fmt;
use mime::Mime;
use mime::TopLevel::Text;
use mime::SubLevel::Plain;
use super::{Headers, Header, Raw, ContentLength, ContentType,
Accept, Host, qitem, SetCookie};
use super::{Headers, Header, Raw, ContentLength, ContentType, Host, SetCookie};

#[cfg(feature = "nightly")]
use test::Bencher;
Expand All @@ -723,25 +719,6 @@ mod tests {
assert_eq!(headers.get(), Some(&ContentLength(10)));
}

#[test]
fn test_content_type() {
let content_type = Header::parse_header(&b"text/plain".as_ref().into());
assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![]))));
}

#[test]
fn test_accept() {
let text_plain = qitem(Mime(Text, Plain, vec![]));
let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap();

let accept = Header::parse_header(&b"text/plain".as_ref().into());
assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()])));

let bytevec = b"application/vnd.github.v3.full+json; q=0.5, text/plain".as_ref().into();
let accept = Header::parse_header(&bytevec);
assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain])));
}

#[derive(Clone, PartialEq, Debug)]
struct CrazyLength(Option<bool>, usize);

Expand Down Expand Up @@ -882,7 +859,7 @@ mod tests {
let mut headers = Headers::new();
headers.set(ContentLength(10));
assert_eq!(headers.len(), 1);
headers.set(ContentType(Mime(Text, Plain, vec![])));
headers.set(ContentType::json());
assert_eq!(headers.len(), 2);
// Redundant, should not increase count.
headers.set(ContentLength(20));
Expand All @@ -893,7 +870,7 @@ mod tests {
fn test_clear() {
let mut headers = Headers::new();
headers.set(ContentLength(10));
headers.set(ContentType(Mime(Text, Plain, vec![])));
headers.set(ContentType::json());
assert_eq!(headers.len(), 2);
headers.clear();
assert_eq!(headers.len(), 0);
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extern crate futures_cpupool;
extern crate httparse;
extern crate language_tags;
#[macro_use] extern crate log;
#[macro_use] pub extern crate mime;
pub extern crate mime;
extern crate base64;
extern crate time;
extern crate tokio_core as tokio;
Expand Down

0 comments on commit f273224

Please sign in to comment.