From f273224f21eedd2f466f12fe30fd24e83c35922c Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 8 Jun 2017 13:00:12 -0700 Subject: [PATCH] feat(mime): upgrade to mime v0.3 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. --- Cargo.toml | 2 +- src/header/common/accept.rs | 75 +++++++++++++++---------------- src/header/common/content_type.rs | 35 +++++++-------- src/header/common/link.rs | 8 ++-- src/header/common/mod.rs | 2 +- src/header/mod.rs | 29 ++---------- src/lib.rs | 2 +- 7 files changed, 59 insertions(+), 94 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9f142f0..f7ef5034 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/header/common/accept.rs b/src/header/common/accept.rs index edd18eca..0e5598a1 100644 --- a/src/header/common/accept.rs +++ b/src/header/common/accept.rs @@ -1,4 +1,4 @@ -use mime::Mime; +use mime::{self, Mime}; use header::{QualityItem, qitem}; @@ -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)+ 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)), ]))); @@ -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)]) } } diff --git a/src/header/common/content_type.rs b/src/header/common/content_type.rs index 41179b56..6ad2ac79 100644 --- a/src/header/common/content_type.rs +++ b/src/header/common/content_type.rs @@ -1,4 +1,4 @@ -use mime::Mime; +use mime::{self, Mime}; header! { /// `Content-Type` header, defined in @@ -22,7 +22,8 @@ header! { /// ``` /// /// # Example values - /// * `text/html; charset=ISO-8859-4` + /// * `text/html; charset=utf-8` + /// * `application/json /// /// # Examples /// ``` @@ -36,13 +37,12 @@ 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] @@ -50,13 +50,8 @@ header! { 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))); } } @@ -64,45 +59,45 @@ 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()] }); diff --git a/src/header/common/link.rs b/src/header/common/link.rs index 2f1a7d81..e374cf7a 100644 --- a/src/header/common/link.rs +++ b/src/header/common/link.rs @@ -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() { @@ -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"; \ rel=\"previous\"; anchor=\"../anchor/example/\"; \ @@ -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]); diff --git a/src/header/common/mod.rs b/src/header/common/mod.rs index 21e6f6ab..6b823589 100644 --- a/src/header/common/mod.rs +++ b/src/header/common/mod.rs @@ -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; diff --git a/src/header/mod.rs b/src/header/mod.rs index 39206f80..d662ad82 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -696,11 +696,7 @@ impl PartialEq 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; @@ -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, usize); @@ -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)); @@ -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); diff --git a/src/lib.rs b/src/lib.rs index 5b0c0f60..b661e58d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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;