feat(headers): adds Accept
Moved utils to shared/. Added quality_value.
This commit is contained in:
		| @@ -1,7 +1,9 @@ | |||||||
| use header::{Header, HeaderFormat}; | use std::fmt; | ||||||
| use std::fmt::{mod, Show}; |  | ||||||
| use std::str::from_utf8; | use header; | ||||||
| use mime::Mime; | use header::shared; | ||||||
|  |  | ||||||
|  | use mime; | ||||||
|  |  | ||||||
| /// The `Accept` header. | /// The `Accept` header. | ||||||
| /// | /// | ||||||
| @@ -14,60 +16,53 @@ use mime::Mime; | |||||||
| /// ``` | /// ``` | ||||||
| /// # use hyper::header::Headers; | /// # use hyper::header::Headers; | ||||||
| /// # use hyper::header::common::Accept; | /// # use hyper::header::common::Accept; | ||||||
|  | /// # use hyper::header::shared::qitem; | ||||||
| /// use hyper::mime::Mime; | /// use hyper::mime::Mime; | ||||||
| /// use hyper::mime::TopLevel::Text; | /// use hyper::mime::TopLevel::Text; | ||||||
| /// use hyper::mime::SubLevel::{Html, Xml}; | /// use hyper::mime::SubLevel::{Html, Xml}; | ||||||
| /// # let mut headers = Headers::new(); | /// # let mut headers = Headers::new(); | ||||||
| /// headers.set(Accept(vec![ Mime(Text, Html, vec![]), Mime(Text, Xml, vec![]) ])); | /// headers.set(Accept(vec![ | ||||||
|  | ///     qitem(Mime(Text, Html, vec![])), | ||||||
|  | ///     qitem(Mime(Text, Xml, vec![])) ])); | ||||||
| /// ``` | /// ``` | ||||||
| #[deriving(Clone, PartialEq, Show)] | #[deriving(Clone, PartialEq, Show)] | ||||||
| pub struct Accept(pub Vec<Mime>); | pub struct Accept(pub Vec<shared::QualityItem<mime::Mime>>); | ||||||
|  |  | ||||||
| deref!(Accept -> Vec<Mime>); | deref!(Accept -> Vec<shared::QualityItem<mime::Mime>>); | ||||||
|  |  | ||||||
| impl Header for Accept { | impl header::Header for Accept { | ||||||
|     fn header_name(_: Option<Accept>) -> &'static str { |     fn header_name(_: Option<Accept>) -> &'static str { | ||||||
|         "Accept" |         "Accept" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<Accept> { |     fn parse_header(raw: &[Vec<u8>]) -> Option<Accept> { | ||||||
|         let mut mimes: Vec<Mime> = vec![]; |         // TODO: Return */* if no value is given. | ||||||
|         for mimes_raw in raw.iter() { |         shared::from_comma_delimited(raw).map(Accept) | ||||||
|             match from_utf8(mimes_raw.as_slice()) { |  | ||||||
|                 Ok(mimes_str) => { |  | ||||||
|                     for mime_str in mimes_str.split(',') { |  | ||||||
|                         match mime_str.trim().parse() { |  | ||||||
|                             Some(mime) => mimes.push(mime), |  | ||||||
|                             None => return None |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }, |  | ||||||
|                 Err(_) => return None |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if !mimes.is_empty() { |  | ||||||
|             Some(Accept(mimes)) |  | ||||||
|         } else { |  | ||||||
|             // Currently is just a None, but later it can be Accept for */* |  | ||||||
|             None |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl HeaderFormat for Accept { | impl header::HeaderFormat for Accept { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         let Accept(ref value) = *self; |         shared::fmt_comma_delimited(fmt, self[]) | ||||||
|         let last = value.len() - 1; |  | ||||||
|         for (i, mime) in value.iter().enumerate() { |  | ||||||
|             try!(mime.fmt(fmt)); |  | ||||||
|             if i < last { |  | ||||||
|                 try!(", ".fmt(fmt)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(bench, Accept, { vec![b"text/plain; q=0.5, text/html".to_vec()] }); | bench_header!(bench, Accept, { vec![b"text/plain; q=0.5, text/html".to_vec()] }); | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_parse_header_no_quality() { | ||||||
|  |     let a: Accept = header::Header::parse_header([b"text/plain; charset=utf-8".to_vec()].as_slice()).unwrap(); | ||||||
|  |     let b = Accept(vec![ | ||||||
|  |         shared::QualityItem{item: mime::Mime(mime::TopLevel::Text, mime::SubLevel::Plain, vec![(mime::Attr::Charset, mime::Value::Utf8)]), quality: 1f32}, | ||||||
|  |     ]); | ||||||
|  |     assert_eq!(a, b); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_parse_header_with_quality() { | ||||||
|  |     let a: Accept = header::Header::parse_header([b"text/plain; charset=utf-8; q=0.5".to_vec()].as_slice()).unwrap(); | ||||||
|  |     let b = Accept(vec![ | ||||||
|  |         shared::QualityItem{item: mime::Mime(mime::TopLevel::Text, mime::SubLevel::Plain, vec![(mime::Attr::Charset, mime::Value::Utf8)]), quality: 0.5f32}, | ||||||
|  |     ]); | ||||||
|  |     assert_eq!(a, b); | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										39
									
								
								src/header/common/accept_encoding.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/header/common/accept_encoding.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | use std::fmt; | ||||||
|  |  | ||||||
|  | use header; | ||||||
|  | use header::shared; | ||||||
|  |  | ||||||
|  | /// The `Accept-Encoding` header | ||||||
|  | /// | ||||||
|  | /// The `Accept-Encoding` header can be used by clients to indicate what | ||||||
|  | /// response encodings they accept. | ||||||
|  | #[deriving(Clone, PartialEq, Show)] | ||||||
|  | pub struct AcceptEncoding(pub Vec<shared::QualityItem<shared::Encoding>>); | ||||||
|  |  | ||||||
|  | deref!(AcceptEncoding -> Vec<shared::QualityItem<shared::Encoding>>); | ||||||
|  |  | ||||||
|  | impl header::Header for AcceptEncoding { | ||||||
|  |     fn header_name(_: Option<AcceptEncoding>) -> &'static str { | ||||||
|  |         "AcceptEncoding" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn parse_header(raw: &[Vec<u8>]) -> Option<AcceptEncoding> { | ||||||
|  |         shared::from_comma_delimited(raw).map(AcceptEncoding) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl header::HeaderFormat for AcceptEncoding { | ||||||
|  |     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         shared::fmt_comma_delimited(fmt, self[]) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_parse_header() { | ||||||
|  |     let a: AcceptEncoding = header::Header::parse_header([b"gzip;q=1.0, identity; q=0.5".to_vec()].as_slice()).unwrap(); | ||||||
|  |     let b = AcceptEncoding(vec![ | ||||||
|  |         shared::QualityItem{item: shared::Gzip, quality: 1f32}, | ||||||
|  |         shared::QualityItem{item: shared::Identity, quality: 0.5f32}, | ||||||
|  |     ]); | ||||||
|  |     assert_eq!(a, b); | ||||||
|  | } | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use method::Method; | use method::Method; | ||||||
| use std::fmt::{mod}; | use std::fmt::{mod}; | ||||||
| use super::util::{from_comma_delimited, fmt_comma_delimited}; | use header::shared::util::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| /// The `Allow` header. | /// The `Allow` header. | ||||||
| /// See also https://tools.ietf.org/html/rfc7231#section-7.4.1 | /// See also https://tools.ietf.org/html/rfc7231#section-7.4.1 | ||||||
| @@ -46,4 +46,3 @@ mod tests { | |||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(bench, Allow, { vec![b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()] }); | bench_header!(bench, Allow, { vec![b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use super::util::{from_one_comma_delimited, fmt_comma_delimited}; | use header::shared::util::{from_one_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| /// The Cache-Control header. | /// The Cache-Control header. | ||||||
| #[deriving(PartialEq, Clone, Show)] | #[deriving(PartialEq, Clone, Show)] | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use super::util::{from_comma_delimited, fmt_comma_delimited}; | use header::shared::util::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| pub use self::ConnectionOption::{KeepAlive, Close, ConnectionHeader}; | pub use self::ConnectionOption::{KeepAlive, Close, ConnectionHeader}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
|  |  | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
|  |  | ||||||
| /// The `Content-Length` header. | /// The `Content-Length` header. | ||||||
| /// | /// | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
| use mime::Mime; | use mime::Mime; | ||||||
|  |  | ||||||
| /// The `Content-Type` header. | /// The `Content-Type` header. | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ use std::fmt::{mod, Show}; | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use time::Tm; | use time::Tm; | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use super::util::{from_one_raw_str, tm_from_str}; | use header::shared::util::from_one_raw_str; | ||||||
|  | use header::shared::time::tm_from_str; | ||||||
|  |  | ||||||
| // Egh, replace as soon as something better than time::Tm exists. | // Egh, replace as soon as something better than time::Tm exists. | ||||||
| /// The `Date` header field. | /// The `Date` header field. | ||||||
| @@ -41,4 +42,3 @@ impl FromStr for Date { | |||||||
| bench_header!(imf_fixdate, Date, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | bench_header!(imf_fixdate, Date, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | ||||||
| bench_header!(rfc_850, Date, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | bench_header!(rfc_850, Date, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | ||||||
| bench_header!(asctime, Date, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | bench_header!(asctime, Date, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod}; | use std::fmt::{mod}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
|  |  | ||||||
| /// The `Etag` header. | /// The `Etag` header. | ||||||
| /// | /// | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ use std::fmt::{mod, Show}; | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use time::Tm; | use time::Tm; | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use super::util::{from_one_raw_str, tm_from_str}; | use header::shared::util::from_one_raw_str; | ||||||
|  | use header::shared::time::tm_from_str; | ||||||
|  |  | ||||||
| /// The `Expires` header field. | /// The `Expires` header field. | ||||||
| #[deriving(Copy, PartialEq, Clone)] | #[deriving(Copy, PartialEq, Clone)] | ||||||
| @@ -40,4 +41,3 @@ impl FromStr for Expires { | |||||||
| bench_header!(imf_fixdate, Expires, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | bench_header!(imf_fixdate, Expires, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | ||||||
| bench_header!(rfc_850, Expires, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | bench_header!(rfc_850, Expires, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | ||||||
| bench_header!(asctime, Expires, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | bench_header!(asctime, Expires, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use Port; | use Port; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
|  |  | ||||||
| /// The `Host` header. | /// The `Host` header. | ||||||
| /// | /// | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ use std::fmt::{mod, Show}; | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use time::Tm; | use time::Tm; | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use super::util::{from_one_raw_str, tm_from_str}; | use header::shared::util::from_one_raw_str; | ||||||
|  | use header::shared::time::tm_from_str; | ||||||
|  |  | ||||||
| /// The `If-Modified-Since` header field. | /// The `If-Modified-Since` header field. | ||||||
| #[deriving(Copy, PartialEq, Clone)] | #[deriving(Copy, PartialEq, Clone)] | ||||||
| @@ -40,4 +41,3 @@ impl FromStr for IfModifiedSince { | |||||||
| bench_header!(imf_fixdate, IfModifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | bench_header!(imf_fixdate, IfModifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | ||||||
| bench_header!(rfc_850, IfModifiedSince, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | bench_header!(rfc_850, IfModifiedSince, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | ||||||
| bench_header!(asctime, IfModifiedSince, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | bench_header!(asctime, IfModifiedSince, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,8 @@ use std::fmt::{mod, Show}; | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use time::Tm; | use time::Tm; | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use super::util::{from_one_raw_str, tm_from_str}; | use header::shared::util::from_one_raw_str; | ||||||
|  | use header::shared::time::tm_from_str; | ||||||
|  |  | ||||||
| /// The `LastModified` header field. | /// The `LastModified` header field. | ||||||
| #[deriving(Copy, PartialEq, Clone)] | #[deriving(Copy, PartialEq, Clone)] | ||||||
| @@ -40,4 +41,3 @@ impl FromStr for LastModified { | |||||||
| bench_header!(imf_fixdate, LastModified, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | bench_header!(imf_fixdate, LastModified, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); | ||||||
| bench_header!(rfc_850, LastModified, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | bench_header!(rfc_850, LastModified, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); | ||||||
| bench_header!(asctime, LastModified, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | bench_header!(asctime, LastModified, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
|  |  | ||||||
| /// The `Location` header. | /// The `Location` header. | ||||||
| /// | /// | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| //! is used, such as `ContentType(pub Mime)`. | //! is used, such as `ContentType(pub Mime)`. | ||||||
|  |  | ||||||
| pub use self::accept::Accept; | pub use self::accept::Accept; | ||||||
|  | pub use self::accept_encoding::AcceptEncoding; | ||||||
| pub use self::allow::Allow; | pub use self::allow::Allow; | ||||||
| pub use self::authorization::Authorization; | pub use self::authorization::Authorization; | ||||||
| pub use self::cache_control::CacheControl; | pub use self::cache_control::CacheControl; | ||||||
| @@ -76,6 +77,9 @@ macro_rules! deref( | |||||||
| /// Exposes the Accept header. | /// Exposes the Accept header. | ||||||
| pub mod accept; | pub mod accept; | ||||||
|  |  | ||||||
|  | /// Exposes the AcceptEncoding header. | ||||||
|  | pub mod accept_encoding; | ||||||
|  |  | ||||||
| /// Exposes the Allow header. | /// Exposes the Allow header. | ||||||
| pub mod allow; | pub mod allow; | ||||||
|  |  | ||||||
| @@ -135,5 +139,3 @@ pub mod user_agent; | |||||||
|  |  | ||||||
| /// Exposes the Vary header. | /// Exposes the Vary header. | ||||||
| pub mod vary; | pub mod vary; | ||||||
|  |  | ||||||
| pub mod util; |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
|  |  | ||||||
| /// The `Server` header field. | /// The `Server` header field. | ||||||
| /// | /// | ||||||
| @@ -28,4 +28,3 @@ impl HeaderFormat for Server { | |||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(bench, Server, { vec![b"Some String".to_vec()] }); | bench_header!(bench, Server, { vec![b"Some String".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -103,16 +103,15 @@ fn test_fmt() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn cookie_jar() { | fn cookie_jar() { | ||||||
|     let jar = CookieJar::new("secret".as_bytes()); |     let jar = CookieJar::new(b"secret"); | ||||||
|     let cookie = Cookie::new("foo".to_string(), "bar".to_string()); |     let cookie = Cookie::new("foo".to_string(), "bar".to_string()); | ||||||
|     jar.encrypted().add(cookie); |     jar.encrypted().add(cookie); | ||||||
|  |  | ||||||
|     let cookies = SetCookie::from_cookie_jar(&jar); |     let cookies = SetCookie::from_cookie_jar(&jar); | ||||||
|  |  | ||||||
|     let mut new_jar = CookieJar::new("secret".as_bytes()); |     let mut new_jar = CookieJar::new(b"secret"); | ||||||
|     cookies.apply_to_cookie_jar(&mut new_jar); |     cookies.apply_to_cookie_jar(&mut new_jar); | ||||||
|  |  | ||||||
|     assert_eq!(jar.encrypted().find("foo"), new_jar.encrypted().find("foo")); |     assert_eq!(jar.encrypted().find("foo"), new_jar.encrypted().find("foo")); | ||||||
|     assert_eq!(jar.iter().collect::<Vec<Cookie>>(), new_jar.iter().collect::<Vec<Cookie>>()); |     assert_eq!(jar.iter().collect::<Vec<Cookie>>(), new_jar.iter().collect::<Vec<Cookie>>()); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use super::util::{from_comma_delimited, fmt_comma_delimited}; | use header::shared::util::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| use self::Encoding::{Chunked, Gzip, Deflate, Compress, EncodingExt}; | use self::Encoding::{Chunked, Gzip, Deflate, Compress, EncodingExt}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use super::util::{from_comma_delimited, fmt_comma_delimited}; | use header::shared::util::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| use self::Protocol::{WebSocket, ProtocolExt}; | use self::Protocol::{WebSocket, ProtocolExt}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use super::util::from_one_raw_str; | use header::shared::util::from_one_raw_str; | ||||||
|  |  | ||||||
| /// The `User-Agent` header field. | /// The `User-Agent` header field. | ||||||
| /// | /// | ||||||
|   | |||||||
| @@ -1,84 +0,0 @@ | |||||||
| //! Utility functions for Header implementations. |  | ||||||
|  |  | ||||||
| use std::str::{FromStr, from_utf8}; |  | ||||||
| use std::fmt::{mod, Show}; |  | ||||||
| use time::{Tm, strptime}; |  | ||||||
|  |  | ||||||
| /// Reads a single raw string when parsing a header |  | ||||||
| pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> { |  | ||||||
|     if raw.len() != 1 { |  | ||||||
|         return None; |  | ||||||
|     } |  | ||||||
|     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |  | ||||||
|     match from_utf8(unsafe { raw[].get_unchecked(0)[] }) { |  | ||||||
|         Ok(s) => FromStr::from_str(s), |  | ||||||
|         Err(_) => None |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Reads a comma-delimited raw header into a Vec. |  | ||||||
| #[inline] |  | ||||||
| pub fn from_comma_delimited<T: FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> { |  | ||||||
|     if raw.len() != 1 { |  | ||||||
|         return None; |  | ||||||
|     } |  | ||||||
|     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |  | ||||||
|     from_one_comma_delimited(unsafe { raw.as_slice().get_unchecked(0).as_slice() }) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Reads a comma-delimited raw string into a Vec. |  | ||||||
| pub fn from_one_comma_delimited<T: FromStr>(raw: &[u8]) -> Option<Vec<T>> { |  | ||||||
|     match from_utf8(raw) { |  | ||||||
|         Ok(s) => { |  | ||||||
|             Some(s.as_slice() |  | ||||||
|                  .split(',') |  | ||||||
|                  .map(|x| x.trim()) |  | ||||||
|                  .filter_map(FromStr::from_str) |  | ||||||
|                  .collect()) |  | ||||||
|         } |  | ||||||
|         Err(_) => None |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Format an array into a comma-delimited string. |  | ||||||
| pub fn fmt_comma_delimited<T: Show>(fmt: &mut fmt::Formatter, parts: &[T]) -> fmt::Result { |  | ||||||
|     let last = parts.len() - 1; |  | ||||||
|     for (i, part) in parts.iter().enumerate() { |  | ||||||
|         try!(part.fmt(fmt)); |  | ||||||
|         if i < last { |  | ||||||
|             try!(", ".fmt(fmt)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     Ok(()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Get a Tm from HTTP date formats. |  | ||||||
| //    Prior to 1995, there were three different formats commonly used by |  | ||||||
| //   servers to communicate timestamps.  For compatibility with old |  | ||||||
| //   implementations, all three are defined here.  The preferred format is |  | ||||||
| //   a fixed-length and single-zone subset of the date and time |  | ||||||
| //   specification used by the Internet Message Format [RFC5322]. |  | ||||||
| // |  | ||||||
| //     HTTP-date    = IMF-fixdate / obs-date |  | ||||||
| // |  | ||||||
| //   An example of the preferred format is |  | ||||||
| // |  | ||||||
| //     Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate |  | ||||||
| // |  | ||||||
| //   Examples of the two obsolete formats are |  | ||||||
| // |  | ||||||
| //     Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format |  | ||||||
| //     Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format |  | ||||||
| // |  | ||||||
| //   A recipient that parses a timestamp value in an HTTP header field |  | ||||||
| //   MUST accept all three HTTP-date formats.  When a sender generates a |  | ||||||
| //   header field that contains one or more timestamps defined as |  | ||||||
| //   HTTP-date, the sender MUST generate those timestamps in the |  | ||||||
| //   IMF-fixdate format. |  | ||||||
| pub fn tm_from_str(s: &str) -> Option<Tm> { |  | ||||||
|     strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { |  | ||||||
|         strptime(s, "%A, %d-%b-%y %T %Z") |  | ||||||
|     }).or_else(|_| { |  | ||||||
|         strptime(s, "%c") |  | ||||||
|     }).ok() |  | ||||||
| } |  | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| use header::{Header, HeaderFormat, CaseInsensitive}; | use header::{Header, HeaderFormat, CaseInsensitive}; | ||||||
| use std::fmt::{mod}; | use std::fmt::{mod}; | ||||||
| use super::util::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str}; | use header::shared::util::{from_comma_delimited, fmt_comma_delimited, from_one_raw_str}; | ||||||
|  |  | ||||||
| /// The `Allow` header. | /// The `Allow` header. | ||||||
| /// See also https://tools.ietf.org/html/rfc7231#section-7.1.4 | /// See also https://tools.ietf.org/html/rfc7231#section-7.1.4 | ||||||
|   | |||||||
| @@ -22,10 +22,13 @@ use http::{mod, LineEnding}; | |||||||
| use {HttpResult}; | use {HttpResult}; | ||||||
|  |  | ||||||
| pub use self::common::*; | pub use self::common::*; | ||||||
|  | pub use self::shared::*; | ||||||
|  |  | ||||||
| /// Common Headers | /// Common Headers | ||||||
| pub mod common; | pub mod common; | ||||||
|  |  | ||||||
|  | pub mod shared; | ||||||
|  |  | ||||||
| /// A trait for any object that will represent a header field and value. | /// A trait for any object that will represent a header field and value. | ||||||
| /// | /// | ||||||
| /// This trait represents the construction and identification of headers, | /// This trait represents the construction and identification of headers, | ||||||
| @@ -193,7 +196,7 @@ impl Headers { | |||||||
|     /// ``` |     /// ``` | ||||||
|     /// # use hyper::header::Headers; |     /// # use hyper::header::Headers; | ||||||
|     /// # let mut headers = Headers::new(); |     /// # let mut headers = Headers::new(); | ||||||
|     /// headers.set_raw("content-length", vec!["5".as_bytes().to_vec()]); |     /// headers.set_raw("content-length", vec![b"5".to_vec()]); | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn set_raw<K: IntoCow<'static, String, str>>(&mut self, name: K, value: Vec<Vec<u8>>) { |     pub fn set_raw<K: IntoCow<'static, String, str>>(&mut self, name: K, value: Vec<Vec<u8>>) { | ||||||
|         self.data.insert(CaseInsensitive(name.into_cow()), MuCell::new(Item::raw(value))); |         self.data.insert(CaseInsensitive(name.into_cow()), MuCell::new(Item::raw(value))); | ||||||
| @@ -518,6 +521,7 @@ mod tests { | |||||||
|     use super::CaseInsensitive; |     use super::CaseInsensitive; | ||||||
|     use super::{Headers, Header, HeaderFormat}; |     use super::{Headers, Header, HeaderFormat}; | ||||||
|     use super::common::{ContentLength, ContentType, Accept, Host}; |     use super::common::{ContentLength, ContentType, Accept, Host}; | ||||||
|  |     use super::shared::{QualityItem}; | ||||||
|  |  | ||||||
|     use test::Bencher; |     use test::Bencher; | ||||||
|  |  | ||||||
| @@ -542,13 +546,13 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_content_type() { |     fn test_content_type() { | ||||||
|         let content_type = Header::parse_header(["text/plain".as_bytes().to_vec()].as_slice()); |         let content_type = Header::parse_header([b"text/plain".to_vec()].as_slice()); | ||||||
|         assert_eq!(content_type, Some(ContentType(Mime(Text, Plain, vec![])))); |         assert_eq!(content_type, Some(ContentType(Mime(Text, Plain, vec![])))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_accept() { |     fn test_accept() { | ||||||
|         let text_plain = Mime(Text, Plain, vec![]); |         let text_plain = QualityItem{item: Mime(Text, Plain, vec![]), quality: 1f32}; | ||||||
|         let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap(); |         let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap(); | ||||||
|  |  | ||||||
|         let accept = Header::parse_header([b"text/plain".to_vec()].as_slice()); |         let accept = Header::parse_header([b"text/plain".to_vec()].as_slice()); | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								src/header/shared/encoding.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/header/shared/encoding.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | //! Provides an Encoding enum. | ||||||
|  |  | ||||||
|  | use std::fmt; | ||||||
|  | use std::str; | ||||||
|  |  | ||||||
|  | pub use self::Encoding::{Chunked, Gzip, Deflate, Compress, Identity, EncodingExt}; | ||||||
|  |  | ||||||
|  | /// A value to represent an encoding used in `Transfer-Encoding` | ||||||
|  | /// or `Accept-Encoding` header. | ||||||
|  | #[deriving(Clone, PartialEq)] | ||||||
|  | pub enum Encoding { | ||||||
|  |     /// The `chunked` encoding. | ||||||
|  |     Chunked, | ||||||
|  |     /// The `gzip` encoding. | ||||||
|  |     Gzip, | ||||||
|  |     /// The `deflate` encoding. | ||||||
|  |     Deflate, | ||||||
|  |     /// The `compress` encoding. | ||||||
|  |     Compress, | ||||||
|  |     /// The `identity` encoding. | ||||||
|  |     Identity, | ||||||
|  |     /// Some other encoding that is less common, can be any String. | ||||||
|  |     EncodingExt(String) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl fmt::Show for Encoding { | ||||||
|  |     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         match *self { | ||||||
|  |             Chunked => "chunked", | ||||||
|  |             Gzip => "gzip", | ||||||
|  |             Deflate => "deflate", | ||||||
|  |             Compress => "compress", | ||||||
|  |             Identity => "identity", | ||||||
|  |             EncodingExt(ref s) => s.as_slice() | ||||||
|  |             }.fmt(fmt) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | impl str::FromStr for Encoding { | ||||||
|  |     fn from_str(s: &str) -> Option<Encoding> { | ||||||
|  |         match s { | ||||||
|  |             "chunked" => Some(Chunked), | ||||||
|  |             "deflate" => Some(Deflate), | ||||||
|  |             "gzip" => Some(Gzip), | ||||||
|  |             "compress" => Some(Compress), | ||||||
|  |             "identity" => Some(Identity), | ||||||
|  |             _ => Some(EncodingExt(s.to_string())) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								src/header/shared/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/header/shared/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | //! Various functions, structs and enums useful for many headers. | ||||||
|  |  | ||||||
|  | pub use self::encoding::Encoding; | ||||||
|  | pub use self::encoding::Encoding::{ | ||||||
|  |     Chunked, | ||||||
|  |     Gzip, | ||||||
|  |     Deflate, | ||||||
|  |     Compress, | ||||||
|  |     Identity, | ||||||
|  |     EncodingExt}; | ||||||
|  |  | ||||||
|  | pub use self::quality_item::QualityItem; | ||||||
|  | pub use self::quality_item::qitem; | ||||||
|  |  | ||||||
|  | pub use self::time::tm_from_str; | ||||||
|  |  | ||||||
|  | pub use self::util::{ | ||||||
|  |     from_one_raw_str, | ||||||
|  |     from_comma_delimited, | ||||||
|  |     from_one_comma_delimited, | ||||||
|  |     fmt_comma_delimited}; | ||||||
|  |  | ||||||
|  | pub mod encoding; | ||||||
|  | pub mod quality_item; | ||||||
|  | pub mod time; | ||||||
|  | pub mod util; | ||||||
							
								
								
									
										123
									
								
								src/header/shared/quality_item.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/header/shared/quality_item.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | //! Provides a struct for quality values. | ||||||
|  | //! | ||||||
|  | //! [RFC7231 Section 5.3.1](https://tools.ietf.org/html/rfc7231#section-5.3.1) | ||||||
|  | //! gives more information on quality values in HTTP header fields. | ||||||
|  |  | ||||||
|  | use std::fmt; | ||||||
|  | use std::str; | ||||||
|  | #[cfg(test)] use super::encoding::*; | ||||||
|  |  | ||||||
|  | /// Represents an item with a quality value as defined in | ||||||
|  | /// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.1). | ||||||
|  | #[deriving(Clone, PartialEq)] | ||||||
|  | pub struct QualityItem<T> { | ||||||
|  |     /// The actual contents of the field. | ||||||
|  |     pub item: T, | ||||||
|  |     /// The quality (client or server preference) for the value. | ||||||
|  |     pub quality: f32, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T> QualityItem<T> { | ||||||
|  |     /// Creates a new `QualityItem` from an item and a quality. | ||||||
|  |     /// The item can be of any type. | ||||||
|  |     /// The quality should be a value in the range [0, 1]. | ||||||
|  |     pub fn new(item: T, quality: f32) -> QualityItem<T> { | ||||||
|  |         QualityItem{item: item, quality: quality} | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T: fmt::Show> fmt::Show for QualityItem<T> { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         write!(f, "{}; q={}", self.item, format!("{:.3}", self.quality).trim_right_matches(['0', '.'].as_slice())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<T: str::FromStr> str::FromStr for QualityItem<T> { | ||||||
|  |     fn from_str(s: &str) -> Option<Self> { | ||||||
|  |         // Set defaults used if parsing fails. | ||||||
|  |         let mut raw_item = s; | ||||||
|  |         let mut quality = 1f32; | ||||||
|  |  | ||||||
|  |         let parts: Vec<&str> = s.rsplitn(1, ';').map(|x| x.trim()).collect(); | ||||||
|  |         if parts.len() == 2 { | ||||||
|  |             let start = parts[0].slice(0, 2); | ||||||
|  |             if start == "q=" || start == "Q=" { | ||||||
|  |                 let q_part = parts[0].slice(2, parts[0].len()); | ||||||
|  |                 if q_part.len() > 5 { | ||||||
|  |                     return None; | ||||||
|  |                 } | ||||||
|  |                 let x: Option<f32> = q_part.parse(); | ||||||
|  |                 match x { | ||||||
|  |                     Some(q_value) => { | ||||||
|  |                         if 0f32 <= q_value && q_value <= 1f32 { | ||||||
|  |                             quality = q_value; | ||||||
|  |                             raw_item = parts[1]; | ||||||
|  |                             } else { | ||||||
|  |                                 return None; | ||||||
|  |                             } | ||||||
|  |                         }, | ||||||
|  |                     None => return None, | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         let x: Option<T> = raw_item.parse(); | ||||||
|  |         match x { | ||||||
|  |             Some(item) => { | ||||||
|  |                 Some(QualityItem{ item: item, quality: quality, }) | ||||||
|  |             }, | ||||||
|  |             None => return None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Convinience function to wrap a value in a `QualityItem` | ||||||
|  | /// Sets `q` to the default 1.0 | ||||||
|  | pub fn qitem<T>(item: T) -> QualityItem<T> { | ||||||
|  |     QualityItem::new(item, 1.0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_show1() { | ||||||
|  |     let x = qitem(Chunked); | ||||||
|  |     assert_eq!(format!("{}", x), "chunked; q=1.000"); | ||||||
|  | } | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_show2() { | ||||||
|  |     let x = QualityItem::new(Chunked, 0.001); | ||||||
|  |     assert_eq!(format!("{}", x), "chunked; q=0.001"); | ||||||
|  | } | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_show3() { | ||||||
|  |     // Custom value | ||||||
|  |     let x = QualityItem{ | ||||||
|  |         item: EncodingExt("identity".to_string()), | ||||||
|  |         quality: 0.5f32, | ||||||
|  |     }; | ||||||
|  |     assert_eq!(format!("{}", x), "identity; q=0.500"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_from_str1() { | ||||||
|  |     let x: Option<QualityItem<Encoding>> = "chunked".parse(); | ||||||
|  |     assert_eq!(x.unwrap(), QualityItem{ item: Chunked, quality: 1f32, }); | ||||||
|  | } | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_from_str2() { | ||||||
|  |     let x: Option<QualityItem<Encoding>> = "chunked; q=1".parse(); | ||||||
|  |     assert_eq!(x.unwrap(), QualityItem{ item: Chunked, quality: 1f32, }); | ||||||
|  | } | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_from_str3() { | ||||||
|  |     let x: Option<QualityItem<Encoding>> = "gzip; q=0.5".parse(); | ||||||
|  |     assert_eq!(x.unwrap(), QualityItem{ item: Gzip, quality: 0.5f32, }); | ||||||
|  | } | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_from_str4() { | ||||||
|  |     let x: Option<QualityItem<Encoding>> = "gzip; q=0.273".parse(); | ||||||
|  |     assert_eq!(x.unwrap(), QualityItem{ item: Gzip, quality: 0.273f32, }); | ||||||
|  | } | ||||||
|  | #[test] | ||||||
|  | fn test_quality_item_from_str5() { | ||||||
|  |     let x: Option<QualityItem<Encoding>> = "gzip; q=0.2739999".parse(); | ||||||
|  |     assert_eq!(x, None); | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								src/header/shared/time.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/header/shared/time.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | //! Provides utility function to parse HTTP header value time. | ||||||
|  |  | ||||||
|  | extern crate time; | ||||||
|  |  | ||||||
|  | /// Get a Tm from HTTP date formats. | ||||||
|  | //    Prior to 1995, there were three different formats commonly used by | ||||||
|  | //   servers to communicate timestamps.  For compatibility with old | ||||||
|  | //   implementations, all three are defined here.  The preferred format is | ||||||
|  | //   a fixed-length and single-zone subset of the date and time | ||||||
|  | //   specification used by the Internet Message Format [RFC5322]. | ||||||
|  | // | ||||||
|  | //     HTTP-date    = IMF-fixdate / obs-date | ||||||
|  | // | ||||||
|  | //   An example of the preferred format is | ||||||
|  | // | ||||||
|  | //     Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate | ||||||
|  | // | ||||||
|  | //   Examples of the two obsolete formats are | ||||||
|  | // | ||||||
|  | //     Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format | ||||||
|  | //     Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format | ||||||
|  | // | ||||||
|  | //   A recipient that parses a timestamp value in an HTTP header field | ||||||
|  | //   MUST accept all three HTTP-date formats.  When a sender generates a | ||||||
|  | //   header field that contains one or more timestamps defined as | ||||||
|  | //   HTTP-date, the sender MUST generate those timestamps in the | ||||||
|  | //   IMF-fixdate format. | ||||||
|  | pub fn tm_from_str(s: &str) -> Option<time::Tm> { | ||||||
|  |     time::strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { | ||||||
|  |         time::strptime(s, "%A, %d-%b-%y %T %Z") | ||||||
|  |     }).or_else(|_| { | ||||||
|  |         time::strptime(s, "%c") | ||||||
|  |     }).ok() | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/header/shared/util.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/header/shared/util.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | //! Utility functions for Header implementations. | ||||||
|  |  | ||||||
|  | use std::str; | ||||||
|  | use std::fmt; | ||||||
|  |  | ||||||
|  | /// Reads a single raw string when parsing a header | ||||||
|  | pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<T> { | ||||||
|  |     if raw.len() != 1 { | ||||||
|  |         return None; | ||||||
|  |     } | ||||||
|  |     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. | ||||||
|  |     match str::from_utf8(unsafe { raw[].unsafe_get(0)[] }) { | ||||||
|  |         Ok(s) => str::FromStr::from_str(s), | ||||||
|  |         Err(_) => None | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Reads a comma-delimited raw header into a Vec. | ||||||
|  | #[inline] | ||||||
|  | pub fn from_comma_delimited<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> { | ||||||
|  |     if raw.len() != 1 { | ||||||
|  |         return None; | ||||||
|  |     } | ||||||
|  |     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. | ||||||
|  |     from_one_comma_delimited(unsafe { raw.as_slice().unsafe_get(0).as_slice() }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Reads a comma-delimited raw string into a Vec. | ||||||
|  | pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> Option<Vec<T>> { | ||||||
|  |     match str::from_utf8(raw) { | ||||||
|  |         Ok(s) => { | ||||||
|  |             Some(s.as_slice() | ||||||
|  |                  .split(',') | ||||||
|  |                  .map(|x| x.trim()) | ||||||
|  |                  .filter_map(str::FromStr::from_str) | ||||||
|  |                  .collect()) | ||||||
|  |         } | ||||||
|  |         Err(_) => None | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Format an array into a comma-delimited string. | ||||||
|  | pub fn fmt_comma_delimited<T: fmt::Show>(fmt: &mut fmt::Formatter, parts: &[T]) -> fmt::Result { | ||||||
|  |     let last = parts.len() - 1; | ||||||
|  |     for (i, part) in parts.iter().enumerate() { | ||||||
|  |         try!(write!(fmt, "{}", part)); | ||||||
|  |         if i < last { | ||||||
|  |             try!(write!(fmt, ", ")); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
| @@ -789,7 +789,7 @@ mod tests { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         read("Host: rust-lang.org\r\n", Ok(Some(("Host".to_string(), |         read("Host: rust-lang.org\r\n", Ok(Some(("Host".to_string(), | ||||||
|                                                 "rust-lang.org".as_bytes().to_vec())))); |                                                  b"rust-lang.org".to_vec())))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user