From c5133a83e950c3be0a6a47fc119d23c724b794fc Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 5 May 2022 22:09:00 -0400 Subject: [PATCH] refactor: remove unused code The removed code is no longer referenced anywhere. Remove it to keep the repository clean. This will additionally make it easier for contributors who are trying to understand which code they need to change. The git history will preserve this code in case some of it needs to be resurrected. Signed-off-by: Nathaniel McCallum --- headers-derive/Cargo.toml | 18 - headers-derive/LICENSE | 1 - headers-derive/README.md | 3 - headers-derive/src/lib.rs | 207 ----- src/disabled/accept.rs | 150 ---- src/disabled/accept_charset.rs | 57 -- src/disabled/accept_encoding.rs | 72 -- src/disabled/accept_language.rs | 72 -- src/disabled/content_language.rs | 35 - src/disabled/from.rs | 29 - src/disabled/last_event_id.rs | 40 - src/disabled/link.rs | 1105 --------------------------- src/disabled/prefer.rs | 210 ----- src/disabled/preference_applied.rs | 110 --- src/disabled/util/charset.rs | 229 ------ src/disabled/util/encoding.rs | 57 -- src/disabled/util/extended_value.rs | 192 ----- src/disabled/util/quality_value.rs | 268 ------- src/disabled/warning.rs | 182 ----- 19 files changed, 3037 deletions(-) delete mode 100644 headers-derive/Cargo.toml delete mode 120000 headers-derive/LICENSE delete mode 100644 headers-derive/README.md delete mode 100644 headers-derive/src/lib.rs delete mode 100644 src/disabled/accept.rs delete mode 100644 src/disabled/accept_charset.rs delete mode 100644 src/disabled/accept_encoding.rs delete mode 100644 src/disabled/accept_language.rs delete mode 100644 src/disabled/content_language.rs delete mode 100644 src/disabled/from.rs delete mode 100644 src/disabled/last_event_id.rs delete mode 100644 src/disabled/link.rs delete mode 100644 src/disabled/prefer.rs delete mode 100644 src/disabled/preference_applied.rs delete mode 100644 src/disabled/util/charset.rs delete mode 100644 src/disabled/util/encoding.rs delete mode 100644 src/disabled/util/extended_value.rs delete mode 100644 src/disabled/util/quality_value.rs delete mode 100644 src/disabled/warning.rs diff --git a/headers-derive/Cargo.toml b/headers-derive/Cargo.toml deleted file mode 100644 index d353059d..00000000 --- a/headers-derive/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "headers-derive" -version = "0.1.1" # don't forget to update html_root_url -description = "derive(Header)" -license = "MIT" -readme = "README.md" -homepage = "https://hyper.rs" -repository = "https://github.com/hyperium/headers" -authors = ["Sean McArthur "] - -[lib] -name = "headers_derive" -proc-macro = true - -[dependencies] -proc-macro2 = "1" -quote = "1" -syn = "1" diff --git a/headers-derive/LICENSE b/headers-derive/LICENSE deleted file mode 120000 index ea5b6064..00000000 --- a/headers-derive/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE \ No newline at end of file diff --git a/headers-derive/README.md b/headers-derive/README.md deleted file mode 100644 index ec8dcc54..00000000 --- a/headers-derive/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Internal derive(Header) macro for `headers` crate - -Doesn't work outside the `headers` crate, nothing to see here. diff --git a/headers-derive/src/lib.rs b/headers-derive/src/lib.rs deleted file mode 100644 index 9805cbfa..00000000 --- a/headers-derive/src/lib.rs +++ /dev/null @@ -1,207 +0,0 @@ -#![recursion_limit = "128"] - -extern crate proc_macro; -extern crate proc_macro2; -#[macro_use] -extern crate quote; -extern crate syn; - -use proc_macro::TokenStream; -use proc_macro2::Span; -use syn::{Data, Fields, Ident, Lit, Meta, NestedMeta}; - -#[proc_macro_derive(Header, attributes(header))] -pub fn derive_header(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - impl_header(&ast).into() -} - -fn impl_header(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { - let fns = match impl_fns(ast) { - Ok(fns) => fns, - Err(msg) => { - return quote! { - compile_error!(#msg); - } - .into(); - } - }; - - let decode = fns.decode; - let encode = fns.encode; - - let ty = &ast.ident; - let hname = fns.name.unwrap_or_else(|| to_header_name(&ty.to_string())); - let hname_ident = Ident::new(&hname, Span::call_site()); - let dummy_const = Ident::new(&format!("_IMPL_HEADER_FOR_{}", hname), Span::call_site()); - let impl_block = quote! { - impl __hc::Header for #ty { - fn name() -> &'static __hc::HeaderName { - &__hc::header::#hname_ident - } - fn decode<'i, I>(values: &mut I) -> Result - where - I: Iterator, - { - #decode - } - fn encode>(&self, values: &mut E) { - #encode - } - } - }; - - quote! { - const #dummy_const: () = { - extern crate headers_core as __hc; - #impl_block - }; - } -} - -struct Fns { - encode: proc_macro2::TokenStream, - decode: proc_macro2::TokenStream, - name: Option, -} - -fn impl_fns(ast: &syn::DeriveInput) -> Result { - let ty = &ast.ident; - - // Only structs are allowed... - let st = match ast.data { - Data::Struct(ref st) => st, - _ => return Err("derive(Header) only works on structs".into()), - }; - - // Check attributes for `#[header(...)]` that may influence the code - // that is generated... - let mut name = None; - for attr in &ast.attrs { - if attr.path.segments.len() != 1 { - continue; - } - if attr.path.segments[0].ident != "header" { - continue; - } - - match attr.parse_meta() { - Ok(Meta::List(list)) => { - for meta in &list.nested { - match meta { - NestedMeta::Meta(Meta::NameValue(ref kv)) - if kv.path.is_ident("name_const") => - { - if name.is_some() { - return Err( - "repeated 'name_const' option in #[header] attribute".into() - ); - } - name = match kv.lit { - Lit::Str(ref s) => Some(s.value()), - _ => { - return Err( - "illegal literal in #[header(name_const = ..)] attribute" - .into(), - ); - } - }; - } - _ => return Err("illegal option in #[header(..)] attribute".into()), - } - } - } - Ok(Meta::NameValue(_)) => return Err("illegal #[header = ..] attribute".into()), - Ok(Meta::Path(_)) => return Err("empty #[header] attributes do nothing".into()), - Err(e) => { - // TODO stringify attribute to return better error - return Err(format!("illegal #[header ??] attribute: {:?}", e)); - } - } - } - - let decode_res = quote! { - ::util::TryFromValues::try_from_values(values) - }; - - let (decode, encode_name) = match st.fields { - Fields::Named(ref fields) => { - if fields.named.len() != 1 { - return Err("derive(Header) doesn't support multiple fields".into()); - } - - let field = fields - .named - .iter() - .next() - .expect("just checked for len() == 1"); - let field_name = field.ident.as_ref().unwrap(); - - let decode = quote! { - #decode_res - .map(|inner| #ty { - #field_name: inner, - }) - }; - - let encode_name = Ident::new(&field_name.to_string(), Span::call_site()); - (decode, Value::Named(encode_name)) - } - Fields::Unnamed(ref fields) => { - if fields.unnamed.len() != 1 { - return Err("derive(Header) doesn't support multiple fields".into()); - } - - let decode = quote! { - #decode_res - .map(#ty) - }; - - (decode, Value::Unnamed) - } - Fields::Unit => return Err("derive(Header) doesn't support unit structs".into()), - }; - - let encode = { - let field = if let Value::Named(field) = encode_name { - quote! { - (&self.#field) - } - } else { - quote! { - (&self.0) - } - }; - quote! { - values.extend(::std::iter::once((#field).into())); - } - }; - - Ok(Fns { - decode, - encode, - name, - }) -} - -fn to_header_name(ty_name: &str) -> String { - let mut out = String::new(); - let mut first = true; - for c in ty_name.chars() { - if first { - out.push(c.to_ascii_uppercase()); - first = false; - } else { - if c.is_uppercase() { - out.push('_'); - } - out.push(c.to_ascii_uppercase()); - } - } - out -} - -enum Value { - Named(Ident), - Unnamed, -} diff --git a/src/disabled/accept.rs b/src/disabled/accept.rs deleted file mode 100644 index 3e8b7739..00000000 --- a/src/disabled/accept.rs +++ /dev/null @@ -1,150 +0,0 @@ -use mime::{self, Mime}; - -use {QualityItem, qitem}; - -header! { - /// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2) - /// - /// The `Accept` header field can be used by user agents to specify - /// response media types that are acceptable. Accept header fields can - /// be used to indicate that the request is specifically limited to a - /// small set of desired types, as in the case of a request for an - /// in-line image - /// - /// # ABNF - /// - /// ```text - /// Accept = #( media-range [ accept-params ] ) - /// - /// media-range = ( "*/*" - /// / ( type "/" "*" ) - /// / ( type "/" subtype ) - /// ) *( OWS ";" OWS parameter ) - /// accept-params = weight *( accept-ext ) - /// accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ] - /// ``` - /// - /// # Example values - /// * `audio/*; q=0.2, audio/basic` - /// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c` - /// - /// # Examples - /// ``` - /// # extern crate headers; - /// extern crate mime; - /// use headers::{Headers, Accept, qitem}; - /// - /// let mut headers = Headers::new(); - /// - /// headers.set( - /// Accept(vec![ - /// qitem(mime::TEXT_HTML), - /// ]) - /// ); - /// ``` - /// - /// ``` - /// # extern crate headers; - /// extern crate mime; - /// use headers::{Headers, Accept, qitem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// Accept(vec![ - /// qitem(mime::APPLICATION_JSON), - /// ]) - /// ); - /// ``` - /// ``` - /// # extern crate headers; - /// extern crate mime; - /// use headers::{Headers, Accept, QualityItem, q, qitem}; - /// - /// let mut headers = Headers::new(); - /// - /// headers.set( - /// Accept(vec![ - /// 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) - /// ), - /// ]) - /// ); - /// ``` - (Accept, ACCEPT) => (QualityItem)+ - - test_accept { - // Tests from the RFC - 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(TEXT_PLAIN, q(500)), - qitem(TEXT_HTML), - QualityItem::new( - "text/x-dvi".parse().unwrap(), - q(800)), - qitem("text/x-c".parse().unwrap()), - ]))); - // Custom tests - test_header!( - test3, - vec![b"text/plain; charset=utf-8"], - Some(Accept(vec![ - qitem(TEXT_PLAIN_UTF_8), - ]))); - test_header!( - test4, - vec![b"text/plain; charset=utf-8; q=0.5"], - Some(Accept(vec![ - QualityItem::new(TEXT_PLAIN_UTF_8, - q(500)), - ]))); - - #[test] - fn test_fuzzing1() { - let raw: Raw = "chunk#;e".into(); - let header = Accept::parse_header(&raw); - assert!(header.is_ok()); - } - } -} - -impl Accept { - /// A constructor to easily create `Accept: */*`. - pub fn star() -> Accept { - Accept(vec![qitem(mime::STAR_STAR)]) - } - - /// A constructor to easily create `Accept: application/json`. - pub fn json() -> Accept { - Accept(vec![qitem(mime::APPLICATION_JSON)]) - } - - /// A constructor to easily create `Accept: text/*`. - pub fn text() -> Accept { - Accept(vec![qitem(mime::TEXT_STAR)]) - } - - /// A constructor to easily create `Accept: image/*`. - pub fn image() -> Accept { - Accept(vec![qitem(mime::IMAGE_STAR)]) - } -} - - -bench_header!(bench, Accept, { vec![b"text/plain; q=0.5, text/html".to_vec()] }); diff --git a/src/disabled/accept_charset.rs b/src/disabled/accept_charset.rs deleted file mode 100644 index 96eec896..00000000 --- a/src/disabled/accept_charset.rs +++ /dev/null @@ -1,57 +0,0 @@ -use {Charset, QualityItem}; - -header! { - /// `Accept-Charset` header, defined in - /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3) - /// - /// The `Accept-Charset` header field can be sent by a user agent to - /// indicate what charsets are acceptable in textual response content. - /// This field allows user agents capable of understanding more - /// comprehensive or special-purpose charsets to signal that capability - /// to an origin server that is capable of representing information in - /// those charsets. - /// - /// # ABNF - /// - /// ```text - /// Accept-Charset = 1#( ( charset / "*" ) [ weight ] ) - /// ``` - /// - /// # Example values - /// * `iso-8859-5, unicode-1-1;q=0.8` - /// - /// # Examples - /// ``` - /// use headers::{Headers, AcceptCharset, Charset, qitem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptCharset(vec![qitem(Charset::Us_Ascii)]) - /// ); - /// ``` - /// ``` - /// use headers::{Headers, AcceptCharset, Charset, q, QualityItem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptCharset(vec![ - /// QualityItem::new(Charset::Us_Ascii, q(900)), - /// QualityItem::new(Charset::Iso_8859_10, q(200)), - /// ]) - /// ); - /// ``` - /// ``` - /// use headers::{Headers, AcceptCharset, Charset, qitem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))]) - /// ); - /// ``` - (AcceptCharset, ACCEPT_CHARSET) => (QualityItem)+ - - test_accept_charset { - /// Testcase from RFC - test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]); - } -} diff --git a/src/disabled/accept_encoding.rs b/src/disabled/accept_encoding.rs deleted file mode 100644 index 5eb3adf5..00000000 --- a/src/disabled/accept_encoding.rs +++ /dev/null @@ -1,72 +0,0 @@ -use {Encoding, QualityItem}; - -header! { - /// `Accept-Encoding` header, defined in - /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.4) - /// - /// The `Accept-Encoding` header field can be used by user agents to - /// indicate what response content-codings are - /// acceptable in the response. An `identity` token is used as a synonym - /// for "no encoding" in order to communicate when no encoding is - /// preferred. - /// - /// # ABNF - /// - /// ```text - /// Accept-Encoding = #( codings [ weight ] ) - /// codings = content-coding / "identity" / "*" - /// ``` - /// - /// # Example values - /// * `compress, gzip` - /// * `` - /// * `*` - /// * `compress;q=0.5, gzip;q=1` - /// * `gzip;q=1.0, identity; q=0.5, *;q=0` - /// - /// # Examples - /// ``` - /// use headers::{Headers, AcceptEncoding, Encoding, qitem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptEncoding(vec![qitem(Encoding::Chunked)]) - /// ); - /// ``` - /// ``` - /// use headers::{Headers, AcceptEncoding, Encoding, qitem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptEncoding(vec![ - /// qitem(Encoding::Chunked), - /// qitem(Encoding::Gzip), - /// qitem(Encoding::Deflate), - /// ]) - /// ); - /// ``` - /// ``` - /// use headers::{Headers, AcceptEncoding, Encoding, QualityItem, q, qitem}; - /// - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptEncoding(vec![ - /// qitem(Encoding::Chunked), - /// QualityItem::new(Encoding::Gzip, q(600)), - /// QualityItem::new(Encoding::EncodingExt("*".to_owned()), q(0)), - /// ]) - /// ); - /// ``` - (AcceptEncoding, ACCEPT_ENCODING) => (QualityItem)* - - test_accept_encoding { - // From the RFC - test_header!(test1, vec![b"compress, gzip"]); - test_header!(test2, vec![b""], Some(AcceptEncoding(vec![]))); - test_header!(test3, vec![b"*"]); - // Note: Removed quality 1 from gzip - test_header!(test4, vec![b"compress;q=0.5, gzip"]); - // Note: Removed quality 1 from gzip - test_header!(test5, vec![b"gzip, identity; q=0.5, *;q=0"]); - } -} diff --git a/src/disabled/accept_language.rs b/src/disabled/accept_language.rs deleted file mode 100644 index dccfdb0b..00000000 --- a/src/disabled/accept_language.rs +++ /dev/null @@ -1,72 +0,0 @@ -use language_tags::LanguageTag; -use QualityItem; - -header! { - /// `Accept-Language` header, defined in - /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5) - /// - /// The `Accept-Language` header field can be used by user agents to - /// indicate the set of natural languages that are preferred in the - /// response. - /// - /// # ABNF - /// - /// ```text - /// Accept-Language = 1#( language-range [ weight ] ) - /// language-range = - /// ``` - /// - /// # Example values - /// * `da, en-gb;q=0.8, en;q=0.7` - /// * `en-us;q=1.0, en;q=0.5, fr` - /// - /// # Examples - /// - /// ``` - /// use headers::{Headers, AcceptLanguage, LanguageTag, qitem}; - /// - /// let mut headers = Headers::new(); - /// let mut langtag: LanguageTag = Default::default(); - /// langtag.language = Some("en".to_owned()); - /// langtag.region = Some("US".to_owned()); - /// headers.set( - /// AcceptLanguage(vec![ - /// qitem(langtag), - /// ]) - /// ); - /// ``` - /// - /// ``` - /// # extern crate headers; - /// # #[macro_use] extern crate language_tags; - /// # use headers::{Headers, AcceptLanguage, QualityItem, q, qitem}; - /// # - /// # fn main() { - /// let mut headers = Headers::new(); - /// headers.set( - /// AcceptLanguage(vec![ - /// qitem(langtag!(da)), - /// QualityItem::new(langtag!(en;;;GB), q(800)), - /// QualityItem::new(langtag!(en), q(700)), - /// ]) - /// ); - /// # } - /// ``` - (AcceptLanguage, ACCEPT_LANGUAGE) => (QualityItem)+ - - test_accept_language { - // From the RFC - test_header!(test1, vec![b"da, en-gb;q=0.8, en;q=0.7"]); - // Own test - test_header!( - test2, vec![b"en-US, en; q=0.5, fr"], - Some(AcceptLanguage(vec![ - qitem("en-US".parse().unwrap()), - QualityItem::new("en".parse().unwrap(), q(500)), - qitem("fr".parse().unwrap()), - ]))); - } -} - -bench_header!(bench, AcceptLanguage, - { vec![b"en-us;q=1.0, en;q=0.5, fr".to_vec()] }); diff --git a/src/disabled/content_language.rs b/src/disabled/content_language.rs deleted file mode 100644 index cca8dcbb..00000000 --- a/src/disabled/content_language.rs +++ /dev/null @@ -1,35 +0,0 @@ -use util::FlatCsv; - -/// `Content-Language` header, defined in -/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2) -/// -/// The `Content-Language` header field describes the natural language(s) -/// of the intended audience for the representation. Note that this -/// might not be equivalent to all the languages used within the -/// representation. -/// -/// # ABNF -/// -/// ```text -/// Content-Language = 1#language-tag -/// ``` -/// -/// # Example values -/// -/// * `da` -/// * `mi, en` -/// -/// # Examples -/// -/// ``` -/// # extern crate headers; -/// #[macro_use] extern crate language_tags; -/// use headers::ContentLanguage; -/// # -/// # fn main() { -/// let con_lang = ContentLanguage::new([langtag!(en)]) -/// # } -/// ``` -#[derive(Clone, Debug, PartialEq, Header)] -pub struct ContentLanguage(FlatCsv); - diff --git a/src/disabled/from.rs b/src/disabled/from.rs deleted file mode 100644 index ed00d8e9..00000000 --- a/src/disabled/from.rs +++ /dev/null @@ -1,29 +0,0 @@ -header! { - /// `From` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.5.1) - /// - /// The `From` header field contains an Internet email address for a - /// human user who controls the requesting user agent. The address ought - /// to be machine-usable. - /// - /// # ABNF - /// - /// ```text - /// From = mailbox - /// mailbox = - /// ``` - /// - /// # Example - /// - /// ``` - /// use headers::{Headers, From}; - /// - /// let mut headers = Headers::new(); - /// headers.set(From("webmaster@example.org".to_owned())); - /// ``` - // FIXME: Maybe use mailbox? - (From, FROM) => [String] - - test_from { - test_header!(test1, vec![b"webmaster@example.org"]); - } -} diff --git a/src/disabled/last_event_id.rs b/src/disabled/last_event_id.rs deleted file mode 100644 index c2499b0a..00000000 --- a/src/disabled/last_event_id.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::fmt; - -use util::HeaderValueString; - -/// `Last-Event-ID` header, defined in -/// [RFC3864](https://html.spec.whatwg.org/multipage/references.html#refsRFC3864) -/// -/// The `Last-Event-ID` header contains information about -/// the last event in an http interaction so that it's easier to -/// track of event state. This is helpful when working -/// with [Server-Sent-Events](http://www.html5rocks.com/en/tutorials/eventsource/basics/). If the connection were to be dropped, for example, it'd -/// be useful to let the server know what the last event you -/// received was. -/// -/// The spec is a String with the id of the last event, it can be -/// an empty string which acts a sort of "reset". -// NOTE: This module is disabled since there is no const LAST_EVENT_ID to be -// used for the `impl Header`. It should be possible to enable this module -// when `HeaderName::from_static` can become a `const fn`. -#[derive(Clone, Debug, PartialEq, Header)] -pub struct LastEventId(HeaderValueString); - - -impl fmt::Display for LastEventId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -#[cfg(test)] -mod tests { - - /* - // Initial state - test_header!(test1, vec![b""]); - // Own testcase - test_header!(test2, vec![b"1"], Some(LastEventId("1".to_owned()))); - */ -} - diff --git a/src/disabled/link.rs b/src/disabled/link.rs deleted file mode 100644 index a6d84944..00000000 --- a/src/disabled/link.rs +++ /dev/null @@ -1,1105 +0,0 @@ -use std::fmt; -use std::borrow::Cow; -use std::str::FromStr; -#[allow(unused, deprecated)] -use std::ascii::AsciiExt; - -use mime::Mime; -use language_tags::LanguageTag; - -use parsing; -use {Header, Raw}; - -/// The `Link` header, defined in -/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5) -/// -/// # ABNF -/// -/// ```text -/// Link = "Link" ":" #link-value -/// link-value = "<" URI-Reference ">" *( ";" link-param ) -/// link-param = ( ( "rel" "=" relation-types ) -/// | ( "anchor" "=" <"> URI-Reference <"> ) -/// | ( "rev" "=" relation-types ) -/// | ( "hreflang" "=" Language-Tag ) -/// | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) ) -/// | ( "title" "=" quoted-string ) -/// | ( "title*" "=" ext-value ) -/// | ( "type" "=" ( media-type | quoted-mt ) ) -/// | ( link-extension ) ) -/// link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] ) -/// | ( ext-name-star "=" ext-value ) -/// ext-name-star = parmname "*" ; reserved for RFC2231-profiled -/// ; extensions. Whitespace NOT -/// ; allowed in between. -/// ptoken = 1*ptokenchar -/// ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "(" -/// | ")" | "*" | "+" | "-" | "." | "/" | DIGIT -/// | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA -/// | "[" | "]" | "^" | "_" | "`" | "{" | "|" -/// | "}" | "~" -/// media-type = type-name "/" subtype-name -/// quoted-mt = <"> media-type <"> -/// relation-types = relation-type -/// | <"> relation-type *( 1*SP relation-type ) <"> -/// relation-type = reg-rel-type | ext-rel-type -/// reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" ) -/// ext-rel-type = URI -/// ``` -/// -/// # Example values -/// -/// `Link: ; rel="previous"; -/// title="previous chapter"` -/// -/// `Link: ; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, -/// ; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel` -/// -/// # Examples -/// -/// ``` -/// use headers::{Headers, Link, LinkValue, RelationType}; -/// -/// let link_value = LinkValue::new("http://example.com/TheBook/chapter2") -/// .push_rel(RelationType::Previous) -/// .set_title("previous chapter"); -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// Link::new(vec![link_value]) -/// ); -/// ``` -#[derive(Clone, PartialEq, Debug)] -pub struct Link { - /// A list of the `link-value`s of the Link entity-header. - values: Vec -} - -/// A single `link-value` of a `Link` header, based on: -/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5) -#[derive(Clone, PartialEq, Debug)] -pub struct LinkValue { - /// Target IRI: `link-value`. - link: Cow<'static, str>, - - /// Forward Relation Types: `rel`. - rel: Option>, - - /// Context IRI: `anchor`. - anchor: Option, - - /// Reverse Relation Types: `rev`. - rev: Option>, - - /// Hint on the language of the result of dereferencing - /// the link: `hreflang`. - href_lang: Option>, - - /// Destination medium or media: `media`. - media_desc: Option>, - - /// Label of the destination of a Link: `title`. - title: Option, - - /// The `title` encoded in a different charset: `title*`. - title_star: Option, - - /// Hint on the media type of the result of dereferencing - /// the link: `type`. - media_type: Option, -} - -/// A Media Descriptors Enum based on: -/// [https://www.w3.org/TR/html401/types.html#h-6.13][url] -/// -/// [url]: https://www.w3.org/TR/html401/types.html#h-6.13 -#[derive(Clone, PartialEq, Debug)] -pub enum MediaDesc { - /// screen. - Screen, - /// tty. - Tty, - /// tv. - Tv, - /// projection. - Projection, - /// handheld. - Handheld, - /// print. - Print, - /// braille. - Braille, - /// aural. - Aural, - /// all. - All, - /// Unrecognized media descriptor extension. - Extension(String) -} - -/// A Link Relation Type Enum based on: -/// [RFC5988](https://tools.ietf.org/html/rfc5988#section-6.2.2) -#[derive(Clone, PartialEq, Debug)] -pub enum RelationType { - /// alternate. - Alternate, - /// appendix. - Appendix, - /// bookmark. - Bookmark, - /// chapter. - Chapter, - /// contents. - Contents, - /// copyright. - Copyright, - /// current. - Current, - /// describedby. - DescribedBy, - /// edit. - Edit, - /// edit-media. - EditMedia, - /// enclosure. - Enclosure, - /// first. - First, - /// glossary. - Glossary, - /// help. - Help, - /// hub. - Hub, - /// index. - Index, - /// last. - Last, - /// latest-version. - LatestVersion, - /// license. - License, - /// next. - Next, - /// next-archive. - NextArchive, - /// payment. - Payment, - /// prev. - Prev, - /// predecessor-version. - PredecessorVersion, - /// previous. - Previous, - /// prev-archive. - PrevArchive, - /// related. - Related, - /// replies. - Replies, - /// section. - Section, - /// self. - RelationTypeSelf, - /// service. - Service, - /// start. - Start, - /// stylesheet. - Stylesheet, - /// subsection. - Subsection, - /// successor-version. - SuccessorVersion, - /// up. - Up, - /// versionHistory. - VersionHistory, - /// via. - Via, - /// working-copy. - WorkingCopy, - /// working-copy-of. - WorkingCopyOf, - /// ext-rel-type. - ExtRelType(String) -} - -//////////////////////////////////////////////////////////////////////////////// -// Struct methods -//////////////////////////////////////////////////////////////////////////////// - -impl Link { - /// Create `Link` from a `Vec`. - pub fn new(link_values: Vec) -> Link { - Link { values: link_values } - } - - /// Get the `Link` header's `LinkValue`s. - pub fn values(&self) -> &[LinkValue] { - self.values.as_ref() - } - - /// Add a `LinkValue` instance to the `Link` header's values. - pub fn push_value(&mut self, link_value: LinkValue) { - self.values.push(link_value); - } -} - -impl LinkValue { - /// Create `LinkValue` from URI-Reference. - pub fn new(uri: T) -> LinkValue - where T: Into> { - LinkValue { - link: uri.into(), - rel: None, - anchor: None, - rev: None, - href_lang: None, - media_desc: None, - title: None, - title_star: None, - media_type: None, - } - } - - /// Get the `LinkValue`'s value. - pub fn link(&self) -> &str { - self.link.as_ref() - } - - /// Get the `LinkValue`'s `rel` parameter(s). - pub fn rel(&self) -> Option<&[RelationType]> { - self.rel.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `anchor` parameter. - pub fn anchor(&self) -> Option<&str> { - self.anchor.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `rev` parameter(s). - pub fn rev(&self) -> Option<&[RelationType]> { - self.rev.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `hreflang` parameter(s). - pub fn href_lang(&self) -> Option<&[LanguageTag]> { - self.href_lang.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `media` parameter(s). - pub fn media_desc(&self) -> Option<&[MediaDesc]> { - self.media_desc.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `title` parameter. - pub fn title(&self) -> Option<&str> { - self.title.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `title*` parameter. - pub fn title_star(&self) -> Option<&str> { - self.title_star.as_ref().map(AsRef::as_ref) - } - - /// Get the `LinkValue`'s `type` parameter. - pub fn media_type(&self) -> Option<&Mime> { - self.media_type.as_ref() - } - - /// Add a `RelationType` to the `LinkValue`'s `rel` parameter. - pub fn push_rel(mut self, rel: RelationType) -> LinkValue { - let mut v = self.rel.take().unwrap_or(Vec::new()); - - v.push(rel); - - self.rel = Some(v); - - self - } - - /// Set `LinkValue`'s `anchor` parameter. - pub fn set_anchor>(mut self, anchor: T) -> LinkValue { - self.anchor = Some(anchor.into()); - - self - } - - /// Add a `RelationType` to the `LinkValue`'s `rev` parameter. - pub fn push_rev(mut self, rev: RelationType) -> LinkValue { - let mut v = self.rev.take().unwrap_or(Vec::new()); - - v.push(rev); - - self.rev = Some(v); - - self - } - - /// Add a `LanguageTag` to the `LinkValue`'s `hreflang` parameter. - pub fn push_href_lang(mut self, language_tag: LanguageTag) -> LinkValue { - let mut v = self.href_lang.take().unwrap_or(Vec::new()); - - v.push(language_tag); - - self.href_lang = Some(v); - - self - } - - /// Add a `MediaDesc` to the `LinkValue`'s `media_desc` parameter. - pub fn push_media_desc(mut self, media_desc: MediaDesc) -> LinkValue { - let mut v = self.media_desc.take().unwrap_or(Vec::new()); - - v.push(media_desc); - - self.media_desc = Some(v); - - self - } - - /// Set `LinkValue`'s `title` parameter. - pub fn set_title>(mut self, title: T) -> LinkValue { - self.title = Some(title.into()); - - self - } - - /// Set `LinkValue`'s `title*` parameter. - pub fn set_title_star>(mut self, title_star: T) -> LinkValue { - self.title_star = Some(title_star.into()); - - self - } - - /// Set `LinkValue`'s `type` parameter. - pub fn set_media_type(mut self, media_type: Mime) -> LinkValue { - self.media_type = Some(media_type); - - self - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Trait implementations -//////////////////////////////////////////////////////////////////////////////// - -impl Header for Link { - fn header_name() -> &'static str { - static NAME: &'static str = "Link"; - NAME - } - - fn parse_header(raw: &Raw) -> ::Result { - // If more that one `Link` headers are present in a request's - // headers they are combined in a single `Link` header containing - // all the `link-value`s present in each of those `Link` headers. - raw.iter() - .map(parsing::from_raw_str::) - .fold(None, |p, c| { - match (p, c) { - (None, c) => Some(c), - (e @ Some(Err(_)), _) => e, - (Some(Ok(mut p)), Ok(c)) => { - p.values.extend(c.values); - - Some(Ok(p)) - }, - _ => Some(Err(::Error::Header)), - } - }) - .unwrap_or(Err(::Error::Header)) - } - - fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result { - f.fmt_line(self) - } -} - -impl fmt::Display for Link { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt_delimited(f, self.values.as_slice(), ", ", ("", "")) - } -} - -impl fmt::Display for LinkValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "<{}>", self.link)); - - if let Some(ref rel) = self.rel { - try!(fmt_delimited(f, rel.as_slice(), " ", ("; rel=\"", "\""))); - } - if let Some(ref anchor) = self.anchor { - try!(write!(f, "; anchor=\"{}\"", anchor)); - } - if let Some(ref rev) = self.rev { - try!(fmt_delimited(f, rev.as_slice(), " ", ("; rev=\"", "\""))); - } - if let Some(ref href_lang) = self.href_lang { - for tag in href_lang { - try!(write!(f, "; hreflang={}", tag)); - } - } - if let Some(ref media_desc) = self.media_desc { - try!(fmt_delimited(f, media_desc.as_slice(), ", ", ("; media=\"", "\""))); - } - if let Some(ref title) = self.title { - try!(write!(f, "; title=\"{}\"", title)); - } - if let Some(ref title_star) = self.title_star { - try!(write!(f, "; title*={}", title_star)); - } - if let Some(ref media_type) = self.media_type { - try!(write!(f, "; type=\"{}\"", media_type)); - } - - Ok(()) - } -} - -impl FromStr for Link { - type Err = ::Error; - - fn from_str(s: &str) -> ::Result { - // Create a split iterator with delimiters: `;`, `,` - let link_split = SplitAsciiUnquoted::new(s, ";,"); - - let mut link_values: Vec = Vec::new(); - - // Loop over the splits parsing the Link header into - // a `Vec` - for segment in link_split { - // Parse the `Target IRI` - // https://tools.ietf.org/html/rfc5988#section-5.1 - if segment.trim().starts_with('<') { - link_values.push( - match verify_and_trim(segment.trim(), (b'<', b'>')) { - Err(_) => return Err(::Error::Header), - Ok(s) => { - LinkValue { - link: s.to_owned().into(), - rel: None, - anchor: None, - rev: None, - href_lang: None, - media_desc: None, - title: None, - title_star: None, - media_type: None, - } - }, - } - ); - } else { - // Parse the current link-value's parameters - let mut link_param_split = segment.splitn(2, '='); - - let link_param_name = match link_param_split.next() { - None => return Err(::Error::Header), - Some(p) => p.trim(), - }; - - let link_header = match link_values.last_mut() { - None => return Err(::Error::Header), - Some(l) => l, - }; - - if "rel".eq_ignore_ascii_case(link_param_name) { - // Parse relation type: `rel`. - // https://tools.ietf.org/html/rfc5988#section-5.3 - if link_header.rel.is_none() { - link_header.rel = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => { - s.trim_matches(|c: char| c == '"' || c.is_whitespace()) - .split(' ') - .map(|t| t.trim().parse()) - .collect::, _>>() - .or_else(|_| Err(::Error::Header)) - .ok() - }, - }; - } - } else if "anchor".eq_ignore_ascii_case(link_param_name) { - // Parse the `Context IRI`. - // https://tools.ietf.org/html/rfc5988#section-5.2 - link_header.anchor = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) { - Err(_) => return Err(::Error::Header), - Ok(a) => Some(String::from(a)), - }, - }; - } else if "rev".eq_ignore_ascii_case(link_param_name) { - // Parse relation type: `rev`. - // https://tools.ietf.org/html/rfc5988#section-5.3 - if link_header.rev.is_none() { - link_header.rev = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => { - s.trim_matches(|c: char| c == '"' || c.is_whitespace()) - .split(' ') - .map(|t| t.trim().parse()) - .collect::, _>>() - .or_else(|_| Err(::Error::Header)) - .ok() - }, - } - } - } else if "hreflang".eq_ignore_ascii_case(link_param_name) { - // Parse target attribute: `hreflang`. - // https://tools.ietf.org/html/rfc5988#section-5.4 - let mut v = link_header.href_lang.take().unwrap_or(Vec::new()); - - v.push( - match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => match s.trim().parse() { - Err(_) => return Err(::Error::Header), - Ok(t) => t, - }, - } - ); - - link_header.href_lang = Some(v); - } else if "media".eq_ignore_ascii_case(link_param_name) { - // Parse target attribute: `media`. - // https://tools.ietf.org/html/rfc5988#section-5.4 - if link_header.media_desc.is_none() { - link_header.media_desc = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => { - s.trim_matches(|c: char| c == '"' || c.is_whitespace()) - .split(',') - .map(|t| t.trim().parse()) - .collect::, _>>() - .or_else(|_| Err(::Error::Header)) - .ok() - }, - }; - } - } else if "title".eq_ignore_ascii_case(link_param_name) { - // Parse target attribute: `title`. - // https://tools.ietf.org/html/rfc5988#section-5.4 - if link_header.title.is_none() { - link_header.title = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) { - Err(_) => return Err(::Error::Header), - Ok(t) => Some(String::from(t)), - }, - }; - } - } else if "title*".eq_ignore_ascii_case(link_param_name) { - // Parse target attribute: `title*`. - // https://tools.ietf.org/html/rfc5988#section-5.4 - // - // Definition of `ext-value`: - // https://tools.ietf.org/html/rfc5987#section-3.2.1 - if link_header.title_star.is_none() { - link_header.title_star = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => Some(String::from(s.trim())), - }; - } - } else if "type".eq_ignore_ascii_case(link_param_name) { - // Parse target attribute: `type`. - // https://tools.ietf.org/html/rfc5988#section-5.4 - if link_header.media_type.is_none() { - link_header.media_type = match link_param_split.next() { - None | Some("") => return Err(::Error::Header), - Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) { - Err(_) => return Err(::Error::Header), - Ok(t) => match t.parse() { - Err(_) => return Err(::Error::Header), - Ok(m) => Some(m), - }, - }, - - }; - } - } else { - return Err(::Error::Header); - } - } - } - - Ok(Link::new(link_values)) - } -} - -impl fmt::Display for MediaDesc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - MediaDesc::Screen => write!(f, "screen"), - MediaDesc::Tty => write!(f, "tty"), - MediaDesc::Tv => write!(f, "tv"), - MediaDesc::Projection => write!(f, "projection"), - MediaDesc::Handheld => write!(f, "handheld"), - MediaDesc::Print => write!(f, "print"), - MediaDesc::Braille => write!(f, "braille"), - MediaDesc::Aural => write!(f, "aural"), - MediaDesc::All => write!(f, "all"), - MediaDesc::Extension(ref other) => write!(f, "{}", other), - } - } -} - -impl FromStr for MediaDesc { - type Err = ::Error; - - fn from_str(s: &str) -> ::Result { - match s { - "screen" => Ok(MediaDesc::Screen), - "tty" => Ok(MediaDesc::Tty), - "tv" => Ok(MediaDesc::Tv), - "projection" => Ok(MediaDesc::Projection), - "handheld" => Ok(MediaDesc::Handheld), - "print" => Ok(MediaDesc::Print), - "braille" => Ok(MediaDesc::Braille), - "aural" => Ok(MediaDesc::Aural), - "all" => Ok(MediaDesc::All), - _ => Ok(MediaDesc::Extension(String::from(s))), - } - } -} - -impl fmt::Display for RelationType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - RelationType::Alternate => write!(f, "alternate"), - RelationType::Appendix => write!(f, "appendix"), - RelationType::Bookmark => write!(f, "bookmark"), - RelationType::Chapter => write!(f, "chapter"), - RelationType::Contents => write!(f, "contents"), - RelationType::Copyright => write!(f, "copyright"), - RelationType::Current => write!(f, "current"), - RelationType::DescribedBy => write!(f, "describedby"), - RelationType::Edit => write!(f, "edit"), - RelationType::EditMedia => write!(f, "edit-media"), - RelationType::Enclosure => write!(f, "enclosure"), - RelationType::First => write!(f, "first"), - RelationType::Glossary => write!(f, "glossary"), - RelationType::Help => write!(f, "help"), - RelationType::Hub => write!(f, "hub"), - RelationType::Index => write!(f, "index"), - RelationType::Last => write!(f, "last"), - RelationType::LatestVersion => write!(f, "latest-version"), - RelationType::License => write!(f, "license"), - RelationType::Next => write!(f, "next"), - RelationType::NextArchive => write!(f, "next-archive"), - RelationType::Payment => write!(f, "payment"), - RelationType::Prev => write!(f, "prev"), - RelationType::PredecessorVersion => write!(f, "predecessor-version"), - RelationType::Previous => write!(f, "previous"), - RelationType::PrevArchive => write!(f, "prev-archive"), - RelationType::Related => write!(f, "related"), - RelationType::Replies => write!(f, "replies"), - RelationType::Section => write!(f, "section"), - RelationType::RelationTypeSelf => write!(f, "self"), - RelationType::Service => write!(f, "service"), - RelationType::Start => write!(f, "start"), - RelationType::Stylesheet => write!(f, "stylesheet"), - RelationType::Subsection => write!(f, "subsection"), - RelationType::SuccessorVersion => write!(f, "successor-version"), - RelationType::Up => write!(f, "up"), - RelationType::VersionHistory => write!(f, "version-history"), - RelationType::Via => write!(f, "via"), - RelationType::WorkingCopy => write!(f, "working-copy"), - RelationType::WorkingCopyOf => write!(f, "working-copy-of"), - RelationType::ExtRelType(ref uri) => write!(f, "{}", uri), - } - } -} - -impl FromStr for RelationType { - type Err = ::Error; - - fn from_str(s: &str) -> ::Result { - if "alternate".eq_ignore_ascii_case(s) { - Ok(RelationType::Alternate) - } else if "appendix".eq_ignore_ascii_case(s) { - Ok(RelationType::Appendix) - } else if "bookmark".eq_ignore_ascii_case(s) { - Ok(RelationType::Bookmark) - } else if "chapter".eq_ignore_ascii_case(s) { - Ok(RelationType::Chapter) - } else if "contents".eq_ignore_ascii_case(s) { - Ok(RelationType::Contents) - } else if "copyright".eq_ignore_ascii_case(s) { - Ok(RelationType::Copyright) - } else if "current".eq_ignore_ascii_case(s) { - Ok(RelationType::Current) - } else if "describedby".eq_ignore_ascii_case(s) { - Ok(RelationType::DescribedBy) - } else if "edit".eq_ignore_ascii_case(s) { - Ok(RelationType::Edit) - } else if "edit-media".eq_ignore_ascii_case(s) { - Ok(RelationType::EditMedia) - } else if "enclosure".eq_ignore_ascii_case(s) { - Ok(RelationType::Enclosure) - } else if "first".eq_ignore_ascii_case(s) { - Ok(RelationType::First) - } else if "glossary".eq_ignore_ascii_case(s) { - Ok(RelationType::Glossary) - } else if "help".eq_ignore_ascii_case(s) { - Ok(RelationType::Help) - } else if "hub".eq_ignore_ascii_case(s) { - Ok(RelationType::Hub) - } else if "index".eq_ignore_ascii_case(s) { - Ok(RelationType::Index) - } else if "last".eq_ignore_ascii_case(s) { - Ok(RelationType::Last) - } else if "latest-version".eq_ignore_ascii_case(s) { - Ok(RelationType::LatestVersion) - } else if "license".eq_ignore_ascii_case(s) { - Ok(RelationType::License) - } else if "next".eq_ignore_ascii_case(s) { - Ok(RelationType::Next) - } else if "next-archive".eq_ignore_ascii_case(s) { - Ok(RelationType::NextArchive) - } else if "payment".eq_ignore_ascii_case(s) { - Ok(RelationType::Payment) - } else if "prev".eq_ignore_ascii_case(s) { - Ok(RelationType::Prev) - } else if "predecessor-version".eq_ignore_ascii_case(s) { - Ok(RelationType::PredecessorVersion) - } else if "previous".eq_ignore_ascii_case(s) { - Ok(RelationType::Previous) - } else if "prev-archive".eq_ignore_ascii_case(s) { - Ok(RelationType::PrevArchive) - } else if "related".eq_ignore_ascii_case(s) { - Ok(RelationType::Related) - } else if "replies".eq_ignore_ascii_case(s) { - Ok(RelationType::Replies) - } else if "section".eq_ignore_ascii_case(s) { - Ok(RelationType::Section) - } else if "self".eq_ignore_ascii_case(s) { - Ok(RelationType::RelationTypeSelf) - } else if "service".eq_ignore_ascii_case(s) { - Ok(RelationType::Service) - } else if "start".eq_ignore_ascii_case(s) { - Ok(RelationType::Start) - } else if "stylesheet".eq_ignore_ascii_case(s) { - Ok(RelationType::Stylesheet) - } else if "subsection".eq_ignore_ascii_case(s) { - Ok(RelationType::Subsection) - } else if "successor-version".eq_ignore_ascii_case(s) { - Ok(RelationType::SuccessorVersion) - } else if "up".eq_ignore_ascii_case(s) { - Ok(RelationType::Up) - } else if "version-history".eq_ignore_ascii_case(s) { - Ok(RelationType::VersionHistory) - } else if "via".eq_ignore_ascii_case(s) { - Ok(RelationType::Via) - } else if "working-copy".eq_ignore_ascii_case(s) { - Ok(RelationType::WorkingCopy) - } else if "working-copy-of".eq_ignore_ascii_case(s) { - Ok(RelationType::WorkingCopyOf) - } else { - Ok(RelationType::ExtRelType(String::from(s))) - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Utilities -//////////////////////////////////////////////////////////////////////////////// - -struct SplitAsciiUnquoted<'a> { - src: &'a str, - pos: usize, - del: &'a str -} - -impl<'a> SplitAsciiUnquoted<'a> { - fn new(s: &'a str, d: &'a str) -> SplitAsciiUnquoted<'a> { - SplitAsciiUnquoted{ - src: s, - pos: 0, - del: d, - } - } -} - -impl<'a> Iterator for SplitAsciiUnquoted<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - if self.pos < self.src.len() { - let prev_pos = self.pos; - let mut pos = self.pos; - - let mut in_quotes = false; - - for c in self.src[prev_pos..].as_bytes().iter() { - in_quotes ^= *c == b'"'; - - // Ignore `c` if we're `in_quotes`. - if !in_quotes && self.del.as_bytes().contains(c) { - break; - } - - pos += 1; - } - - self.pos = pos + 1; - - Some(&self.src[prev_pos..pos]) - } else { - None - } - } -} - -fn fmt_delimited(f: &mut fmt::Formatter, p: &[T], d: &str, b: (&str, &str)) -> fmt::Result { - if p.len() != 0 { - // Write a starting string `b.0` before the first element - try!(write!(f, "{}{}", b.0, p[0])); - - for i in &p[1..] { - // Write the next element preceded by the delimiter `d` - try!(write!(f, "{}{}", d, i)); - } - - // Write a ending string `b.1` before the first element - try!(write!(f, "{}", b.1)); - } - - Ok(()) -} - -fn verify_and_trim(s: &str, b: (u8, u8)) -> ::Result<&str> { - let length = s.len(); - let byte_array = s.as_bytes(); - - // Verify that `s` starts with `b.0` and ends with `b.1` and return - // the contained substring after trimming whitespace. - if length > 1 && b.0 == byte_array[0] && b.1 == byte_array[length - 1] { - Ok(s.trim_matches( - |c: char| c == b.0 as char || c == b.1 as char || c.is_whitespace()) - ) - } else { - Err(::Error::Header) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Tests -//////////////////////////////////////////////////////////////////////////////// - -#[cfg(test)] -mod tests { - use std::fmt; - use std::fmt::Write; - - use super::{Link, LinkValue, MediaDesc, RelationType, SplitAsciiUnquoted}; - use super::{fmt_delimited, verify_and_trim}; - - use Header; - - // use proto::ServerTransaction; - use bytes::BytesMut; - - use mime; - - #[test] - fn test_link() { - let link_value = LinkValue::new("http://example.com/TheBook/chapter2") - .push_rel(RelationType::Previous) - .push_rev(RelationType::Next) - .set_title("previous chapter"); - - let link_header = b"; \ - rel=\"previous\"; rev=next; title=\"previous chapter\""; - - let expected_link = Link::new(vec![link_value]); - - let link = Header::parse_header(&vec![link_header.to_vec()].into()); - assert_eq!(link.ok(), Some(expected_link)); - } - - #[test] - fn test_link_multiple_values() { - let first_link = LinkValue::new("/TheBook/chapter2") - .push_rel(RelationType::Previous) - .set_title_star("UTF-8'de'letztes%20Kapitel"); - - let second_link = LinkValue::new("/TheBook/chapter4") - .push_rel(RelationType::Next) - .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel"); - - let link_header = b"; \ - rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \ - ; \ - rel=\"next\"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel"; - - let expected_link = Link::new(vec![first_link, second_link]); - - let link = Header::parse_header(&vec![link_header.to_vec()].into()); - assert_eq!(link.ok(), Some(expected_link)); - } - - #[test] - fn test_link_all_attributes() { - let link_value = LinkValue::new("http://example.com/TheBook/chapter2") - .push_rel(RelationType::Previous) - .set_anchor("../anchor/example/") - .push_rev(RelationType::Next) - .push_href_lang("de".parse().unwrap()) - .push_media_desc(MediaDesc::Screen) - .set_title("previous chapter") - .set_title_star("title* unparsed") - .set_media_type(mime::TEXT_PLAIN); - - let link_header = b"; \ - rel=\"previous\"; anchor=\"../anchor/example/\"; \ - rev=\"next\"; hreflang=de; media=\"screen\"; \ - title=\"previous chapter\"; title*=title* unparsed; \ - type=\"text/plain\""; - - let expected_link = Link::new(vec![link_value]); - - let link = Header::parse_header(&vec![link_header.to_vec()].into()); - assert_eq!(link.ok(), Some(expected_link)); - } - - // TODO - // #[test] - // fn test_link_multiple_link_headers() { - // let first_link = LinkValue::new("/TheBook/chapter2") - // .push_rel(RelationType::Previous) - // .set_title_star("UTF-8'de'letztes%20Kapitel"); - - // let second_link = LinkValue::new("/TheBook/chapter4") - // .push_rel(RelationType::Next) - // .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel"); - - // let third_link = LinkValue::new("http://example.com/TheBook/chapter2") - // .push_rel(RelationType::Previous) - // .push_rev(RelationType::Next) - // .set_title("previous chapter"); - - // let expected_link = Link::new(vec![first_link, second_link, third_link]); - - // let mut raw = BytesMut::from(b"GET /super_short_uri/and_whatever HTTP/1.1\r\nHost: \ - // hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \ - // utf8\r\nAccept-Encoding: *\r\nLink: ; \ - // rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \ - // ; rel=\"next\"; title*=\ - // UTF-8'de'n%c3%a4chstes%20Kapitel\r\n\ - // Access-Control-Allow-Credentials: None\r\nLink: \ - // ; \ - // rel=\"previous\"; rev=next; title=\"previous chapter\"\ - // \r\n\r\n".to_vec()); - - // let (mut res, _) = ServerTransaction::parse(&mut raw).unwrap().unwrap(); - - // let link = res.headers.remove::().unwrap(); - - // assert_eq!(link, expected_link); - // } - - #[test] - fn test_link_display() { - let link_value = LinkValue::new("http://example.com/TheBook/chapter2") - .push_rel(RelationType::Previous) - .set_anchor("/anchor/example/") - .push_rev(RelationType::Next) - .push_href_lang("de".parse().unwrap()) - .push_media_desc(MediaDesc::Screen) - .set_title("previous chapter") - .set_title_star("title* unparsed") - .set_media_type(mime::TEXT_PLAIN); - - let link = Link::new(vec![link_value]); - - let mut link_header = String::new(); - write!(&mut link_header, "{}", link).unwrap(); - - let expected_link_header = "; \ - rel=\"previous\"; anchor=\"/anchor/example/\"; \ - rev=\"next\"; hreflang=de; media=\"screen\"; \ - title=\"previous chapter\"; title*=title* unparsed; \ - type=\"text/plain\""; - - assert_eq!(link_header, expected_link_header); - } - - #[test] - fn test_link_parsing_errors() { - let link_a = b"http://example.com/TheBook/chapter2; \ - rel=\"previous\"; rev=next; title=\"previous chapter\""; - - let mut err: Result = Header::parse_header(&vec![link_a.to_vec()].into()); - assert_eq!(err.is_err(), true); - - let link_b = b"; \ - =\"previous\"; rev=next; title=\"previous chapter\""; - - err = Header::parse_header(&vec![link_b.to_vec()].into()); - assert_eq!(err.is_err(), true); - - let link_c = b"; \ - rel=; rev=next; title=\"previous chapter\""; - - err = Header::parse_header(&vec![link_c.to_vec()].into()); - assert_eq!(err.is_err(), true); - - let link_d = b"; \ - rel=\"previous\"; rev=next; title="; - - err = Header::parse_header(&vec![link_d.to_vec()].into()); - assert_eq!(err.is_err(), true); - - let link_e = b"; \ - rel=\"previous\"; rev=next; attr=unknown"; - - err = Header::parse_header(&vec![link_e.to_vec()].into()); - assert_eq!(err.is_err(), true); - } - - #[test] - fn test_link_split_ascii_unquoted_iterator() { - let string = "some, text; \"and, more; in quotes\", or not"; - let mut string_split = SplitAsciiUnquoted::new(string, ";,"); - - assert_eq!(Some("some"), string_split.next()); - assert_eq!(Some(" text"), string_split.next()); - assert_eq!(Some(" \"and, more; in quotes\""), string_split.next()); - assert_eq!(Some(" or not"), string_split.next()); - assert_eq!(None, string_split.next()); - } - - #[test] - fn test_link_fmt_delimited() { - struct TestFormatterStruct<'a> { v: Vec<&'a str> }; - - impl<'a> fmt::Display for TestFormatterStruct<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt_delimited(f, self.v.as_slice(), ", ", (">>", "<<")) - } - } - - let test_formatter = TestFormatterStruct { v: vec!["first", "second"] }; - - let mut string = String::new(); - write!(&mut string, "{}", test_formatter).unwrap(); - - let expected_string = ">>first, second<<"; - - assert_eq!(string, expected_string); - } - - #[test] - fn test_link_verify_and_trim() { - let string = verify_and_trim("> some string <", (b'>', b'<')); - assert_eq!(string.ok(), Some("some string")); - - let err = verify_and_trim(" > some string <", (b'>', b'<')); - assert_eq!(err.is_err(), true); - } -} - -bench_header!(bench_link, Link, { vec![b"; rel=\"previous\"; rev=next; title=\"previous chapter\"; type=\"text/html\"; media=\"screen, tty\"".to_vec()] }); diff --git a/src/disabled/prefer.rs b/src/disabled/prefer.rs deleted file mode 100644 index a6e4dc37..00000000 --- a/src/disabled/prefer.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::fmt; -use std::str::FromStr; -use {Header, Raw}; -use parsing::{from_comma_delimited, fmt_comma_delimited}; - -/// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) -/// -/// The `Prefer` header field can be used by a client to request that certain -/// behaviors be employed by a server while processing a request. -/// -/// # ABNF -/// -/// ```text -/// Prefer = "Prefer" ":" 1#preference -/// preference = token [ BWS "=" BWS word ] -/// *( OWS ";" [ OWS parameter ] ) -/// parameter = token [ BWS "=" BWS word ] -/// ``` -/// -/// # Example values -/// * `respond-async` -/// * `return=minimal` -/// * `wait=30` -/// -/// # Examples -/// -/// ``` -/// use headers::{Headers, Prefer, Preference}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// Prefer(vec![Preference::RespondAsync]) -/// ); -/// ``` -/// -/// ``` -/// use headers::{Headers, Prefer, Preference}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// Prefer(vec![ -/// Preference::RespondAsync, -/// Preference::ReturnRepresentation, -/// Preference::Wait(10u32), -/// Preference::Extension("foo".to_owned(), -/// "bar".to_owned(), -/// vec![]), -/// ]) -/// ); -/// ``` -#[derive(PartialEq, Clone, Debug)] -pub struct Prefer(pub Vec); - -__hyper__deref!(Prefer => Vec); - -impl Header for Prefer { - fn header_name() -> &'static str { - static NAME: &'static str = "Prefer"; - NAME - } - - fn parse_header(raw: &Raw) -> ::Result { - let preferences = try!(from_comma_delimited(raw)); - if !preferences.is_empty() { - Ok(Prefer(preferences)) - } else { - Err(::Error::Header) - } - } - - fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result { - f.fmt_line(self) - } -} - -impl fmt::Display for Prefer { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt_comma_delimited(f, &self[..]) - } -} - -/// Prefer contains a list of these preferences. -#[derive(PartialEq, Clone, Debug)] -pub enum Preference { - /// "respond-async" - RespondAsync, - /// "return=representation" - ReturnRepresentation, - /// "return=minimal" - ReturnMinimal, - /// "handling=strict" - HandlingStrict, - /// "handling=lenient" - HandlingLenient, - /// "wait=delta" - Wait(u32), - - /// Extension preferences. Always has a value, if none is specified it is - /// just "". A preference can also have a list of parameters. - Extension(String, String, Vec<(String, String)>) -} - -impl fmt::Display for Preference { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Preference::*; - fmt::Display::fmt(match *self { - RespondAsync => "respond-async", - ReturnRepresentation => "return=representation", - ReturnMinimal => "return=minimal", - HandlingStrict => "handling=strict", - HandlingLenient => "handling=lenient", - - Wait(secs) => return write!(f, "wait={}", secs), - - Extension(ref name, ref value, ref params) => { - try!(write!(f, "{}", name)); - if value != "" { try!(write!(f, "={}", value)); } - if !params.is_empty() { - for &(ref name, ref value) in params { - try!(write!(f, "; {}", name)); - if value != "" { try!(write!(f, "={}", value)); } - } - } - return Ok(()); - } - }, f) - } -} - -impl FromStr for Preference { - type Err = Option<::Err>; - fn from_str(s: &str) -> Result::Err>> { - use self::Preference::*; - let mut params = s.split(';').map(|p| { - let mut param = p.splitn(2, '='); - match (param.next(), param.next()) { - (Some(name), Some(value)) => (name.trim(), value.trim().trim_matches('"')), - (Some(name), None) => (name.trim(), ""), - // This can safely be unreachable because the [`splitn`][1] - // function (used above) will always have at least one value. - // - // [1]: http://doc.rust-lang.org/std/primitive.str.html#method.splitn - _ => { unreachable!(); } - } - }); - match params.nth(0) { - Some(param) => { - let rest: Vec<(String, String)> = params.map(|(l, r)| (l.to_owned(), r.to_owned())).collect(); - match param { - ("respond-async", "") => if rest.is_empty() { Ok(RespondAsync) } else { Err(None) }, - ("return", "representation") => if rest.is_empty() { Ok(ReturnRepresentation) } else { Err(None) }, - ("return", "minimal") => if rest.is_empty() { Ok(ReturnMinimal) } else { Err(None) }, - ("handling", "strict") => if rest.is_empty() { Ok(HandlingStrict) } else { Err(None) }, - ("handling", "lenient") => if rest.is_empty() { Ok(HandlingLenient) } else { Err(None) }, - ("wait", secs) => if rest.is_empty() { secs.parse().map(Wait).map_err(Some) } else { Err(None) }, - (left, right) => Ok(Extension(left.to_owned(), right.to_owned(), rest)) - } - }, - None => Err(None) - } - } -} - -#[cfg(test)] -mod tests { - use Header; - use super::*; - - #[test] - fn test_parse_multiple_headers() { - let prefer = Header::parse_header(&"respond-async, return=representation".into()); - assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync, - Preference::ReturnRepresentation]))) - } - - #[test] - fn test_parse_argument() { - let prefer = Header::parse_header(&"wait=100, handling=lenient, respond-async".into()); - assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100), - Preference::HandlingLenient, - Preference::RespondAsync]))) - } - - #[test] - fn test_parse_quote_form() { - let prefer = Header::parse_header(&"wait=\"200\", handling=\"strict\"".into()); - assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200), - Preference::HandlingStrict]))) - } - - #[test] - fn test_parse_extension() { - let prefer = Header::parse_header(&"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".into()); - assert_eq!(prefer.ok(), Some(Prefer(vec![ - Preference::Extension("foo".to_owned(), "".to_owned(), vec![]), - Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]), - Preference::Extension("baz".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned()), ("bar".to_owned(), "baz".to_owned())]), - Preference::Extension("bux".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned())]), - Preference::Extension("buz".to_owned(), "some parameter".to_owned(), vec![])]))) - } - - #[test] - fn test_fail_with_args() { - let prefer: ::Result = Header::parse_header(&"respond-async; foo=bar".into()); - assert_eq!(prefer.ok(), None); - } -} - -bench_header!(normal, - Prefer, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] }); diff --git a/src/disabled/preference_applied.rs b/src/disabled/preference_applied.rs deleted file mode 100644 index b368bc9b..00000000 --- a/src/disabled/preference_applied.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::fmt; -use {Header, Raw, Preference}; -use parsing::{from_comma_delimited, fmt_comma_delimited}; - -/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) -/// -/// The `Preference-Applied` response header may be included within a -/// response message as an indication as to which `Prefer` header tokens were -/// honored by the server and applied to the processing of a request. -/// -/// # ABNF -/// -/// ```text -/// Preference-Applied = "Preference-Applied" ":" 1#applied-pref -/// applied-pref = token [ BWS "=" BWS word ] -/// ``` -/// -/// # Example values -/// -/// * `respond-async` -/// * `return=minimal` -/// * `wait=30` -/// -/// # Examples -/// -/// ``` -/// use headers::{Headers, PreferenceApplied, Preference}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// PreferenceApplied(vec![Preference::RespondAsync]) -/// ); -/// ``` -/// -/// ``` -/// use headers::{Headers, PreferenceApplied, Preference}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// PreferenceApplied(vec![ -/// Preference::RespondAsync, -/// Preference::ReturnRepresentation, -/// Preference::Wait(10u32), -/// Preference::Extension("foo".to_owned(), -/// "bar".to_owned(), -/// vec![]), -/// ]) -/// ); -/// ``` -#[derive(PartialEq, Clone, Debug)] -pub struct PreferenceApplied(pub Vec); - -__hyper__deref!(PreferenceApplied => Vec); - -impl Header for PreferenceApplied { - fn header_name() -> &'static str { - static NAME: &'static str = "Preference-Applied"; - NAME - } - - fn parse_header(raw: &Raw) -> ::Result { - let preferences = try!(from_comma_delimited(raw)); - if !preferences.is_empty() { - Ok(PreferenceApplied(preferences)) - } else { - Err(::Error::Header) - } - } - - fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result { - f.fmt_line(self) - } -} - -impl fmt::Display for PreferenceApplied { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - //TODO: format this without allocating a Vec and cloning contents - let preferences: Vec<_> = self.0.iter().map(|pref| match pref { - // The spec ignores parameters in `Preferences-Applied` - &Preference::Extension(ref name, ref value, _) => Preference::Extension( - name.to_owned(), - value.to_owned(), - vec![] - ), - preference => preference.clone() - }).collect(); - fmt_comma_delimited(f, &preferences) - } -} - -#[cfg(test)] -mod tests { - use Preference; - use super::*; - - #[test] - fn test_format_ignore_parameters() { - assert_eq!( - format!("{}", PreferenceApplied(vec![Preference::Extension( - "foo".to_owned(), - "bar".to_owned(), - vec![("bar".to_owned(), "foo".to_owned()), ("buz".to_owned(), "".to_owned())] - )])), - "foo=bar".to_owned() - ); - } -} - -bench_header!(normal, - PreferenceApplied, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] }); diff --git a/src/disabled/util/charset.rs b/src/disabled/util/charset.rs deleted file mode 100644 index 5a3f4628..00000000 --- a/src/disabled/util/charset.rs +++ /dev/null @@ -1,229 +0,0 @@ -use std::fmt; -use std::str::FromStr; - -/// A Mime charset. -/// -/// The string representation is normalised to upper case. -/// -/// See [http://www.iana.org/assignments/character-sets/character-sets.xhtml][url]. -/// -/// [url]: http://www.iana.org/assignments/character-sets/character-sets.xhtml -#[derive(Clone, PartialEq)] -pub struct Charset(Charset_); - -impl Charset { - /// US ASCII - pub const US_ASCII: Charset = Charset(Charset_::Us_Ascii); - - /// ISO-8859-1 - pub const ISO_8859_1: Charset = Charset(Charset_::Iso_8859_1); - - /// ISO-8859-2 - pub const ISO_8859_2: Charset = Charset(Charset_::Iso_8859_2); - - /// ISO-8859-3 - pub const ISO_8859_3: Charset = Charset(Charset_::Iso_8859_3); - - /// ISO-8859-4 - pub const ISO_8859_4: Charset = Charset(Charset_::Iso_8859_4); - - /// ISO-8859-5 - pub const ISO_8859_5: Charset = Charset(Charset_::Iso_8859_5); - - /// ISO-8859-6 - pub const ISO_8859_6: Charset = Charset(Charset_::Iso_8859_6); - - /// ISO-8859-7 - pub const ISO_8859_7: Charset = Charset(Charset_::Iso_8859_7); - - /// ISO-8859-8 - pub const ISO_8859_8: Charset = Charset(Charset_::Iso_8859_8); - - /// ISO-8859-9 - pub const ISO_8859_9: Charset = Charset(Charset_::Iso_8859_9); - - /// ISO-8859-10 - pub const ISO_8859_10: Charset = Charset(Charset_::Iso_8859_10); - - /// Shift_JIS - pub const SHIFT_JIS: Charset = Charset(Charset_::Shift_Jis); - - /// EUC-JP - pub const EUC_JP: Charset = Charset(Charset_::Euc_Jp); - - /// ISO-2022-KR - pub const ISO_2022_KR: Charset = Charset(Charset_::Iso_2022_Kr); - - /// EUC-KR - pub const EUC_KR: Charset: Charset(Charset_::Euc_Kr); - - /// ISO-2022-JP - pub const ISO_2022_JP: Charset = Charset(Charset_::Iso_2022_Jp); - - /// ISO-2022-JP-2 - pub const ISO_2022_JP_2: Charset = Charset(Charset_::Iso_2022_Jp_2); - - /// ISO-8859-6-E - pub const ISO_8859_6_E: Charset = Charset(Charset_::Iso_8859_6_E); - - /// ISO-8859-6-I - pub const ISO_8859_6_I: Charset = Charset(Charset_::Iso_8859_6_I); - - /// ISO-8859-8-E - pub const ISO_8859_8_E: Charset = Charset(Charset_::Iso_8859_8_E); - - /// ISO-8859-8-I - pub const ISO_8859_8_I: Charset = Charset(Charset_::Iso_8859_8_I); - - /// GB2312 - pub const GB_2312: Charset = Charset(Charset_::Gb2312); - - /// Big5 - pub const BIG_5: Charset = Charset(Charset_::Big5); - - /// KOI8-R - pub const KOI8_R: Charset = Charset(Charset_::Koi8_R); -} - -#[derive(Clone, Debug, PartialEq)] -#[allow(non_camel_case_types)] -enum Charset_ { - /// US ASCII - Us_Ascii, - /// ISO-8859-1 - Iso_8859_1, - /// ISO-8859-2 - Iso_8859_2, - /// ISO-8859-3 - Iso_8859_3, - /// ISO-8859-4 - Iso_8859_4, - /// ISO-8859-5 - Iso_8859_5, - /// ISO-8859-6 - Iso_8859_6, - /// ISO-8859-7 - Iso_8859_7, - /// ISO-8859-8 - Iso_8859_8, - /// ISO-8859-9 - Iso_8859_9, - /// ISO-8859-10 - Iso_8859_10, - /// Shift_JIS - Shift_Jis, - /// EUC-JP - Euc_Jp, - /// ISO-2022-KR - Iso_2022_Kr, - /// EUC-KR - Euc_Kr, - /// ISO-2022-JP - Iso_2022_Jp, - /// ISO-2022-JP-2 - Iso_2022_Jp_2, - /// ISO-8859-6-E - Iso_8859_6_E, - /// ISO-8859-6-I - Iso_8859_6_I, - /// ISO-8859-8-E - Iso_8859_8_E, - /// ISO-8859-8-I - Iso_8859_8_I, - /// GB2312 - Gb2312, - /// Big5 - Big5, - /// KOI8-R - Koi8_R, - - _Unknown, -} - -impl Charset { - fn name(&self) -> &'static str { - match self.0 { - Charset_::Us_Ascii => "US-ASCII", - Charset_::Iso_8859_1 => "ISO-8859-1", - Charset_::Iso_8859_2 => "ISO-8859-2", - Charset_::Iso_8859_3 => "ISO-8859-3", - Charset_::Iso_8859_4 => "ISO-8859-4", - Charset_::Iso_8859_5 => "ISO-8859-5", - Charset_::Iso_8859_6 => "ISO-8859-6", - Charset_::Iso_8859_7 => "ISO-8859-7", - Charset_::Iso_8859_8 => "ISO-8859-8", - Charset_::Iso_8859_9 => "ISO-8859-9", - Charset_::Iso_8859_10 => "ISO-8859-10", - Charset_::Shift_Jis => "Shift-JIS", - Charset_::Euc_Jp => "EUC-JP", - Charset_::Iso_2022_Kr => "ISO-2022-KR", - Charset_::Euc_Kr => "EUC-KR", - Charset_::Iso_2022_Jp => "ISO-2022-JP", - Charset_::Iso_2022_Jp_2 => "ISO-2022-JP-2", - Charset_::Iso_8859_6_E => "ISO-8859-6-E", - Charset_::Iso_8859_6_I => "ISO-8859-6-I", - Charset_::Iso_8859_8_E => "ISO-8859-8-E", - Charset_::Iso_8859_8_I => "ISO-8859-8-I", - Charset_::Gb2312 => "GB2312", - Charset_::Big5 => "5", - Charset_::Koi8_R => "KOI8-R", - Charset_::_Unknown => unreachable!("Charset::_Unknown"), - } - } -} - -impl fmt::Display for Charset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.name()) - } -} - -#[derive(Debug)] -pub struct CharsetFromStrError(()); - -impl FromStr for Charset { - type Err = CharsetFromStrError; - fn from_str(s: &str) -> Result { - Ok(Charset(match s.to_ascii_uppercase().as_ref() { - "US-ASCII" => Charset_::Us_Ascii, - "ISO-8859-1" => Charset_::Iso_8859_1, - "ISO-8859-2" => Charset_::Iso_8859_2, - "ISO-8859-3" => Charset_::Iso_8859_3, - "ISO-8859-4" => Charset_::Iso_8859_4, - "ISO-8859-5" => Charset_::Iso_8859_5, - "ISO-8859-6" => Charset_::Iso_8859_6, - "ISO-8859-7" => Charset_::Iso_8859_7, - "ISO-8859-8" => Charset_::Iso_8859_8, - "ISO-8859-9" => Charset_::Iso_8859_9, - "ISO-8859-10" => Charset_::Iso_8859_10, - "SHIFT-JIS" => Charset_::Shift_Jis, - "EUC-JP" => Charset_::Euc_Jp, - "ISO-2022-KR" => Charset_::Iso_2022_Kr, - "EUC-KR" => Charset_::Euc_Kr, - "ISO-2022-JP" => Charset_::Iso_2022_Jp, - "ISO-2022-JP-2" => Charset_::Iso_2022_Jp_2, - "ISO-8859-6-E" => Charset_::Iso_8859_6_E, - "ISO-8859-6-I" => Charset_::Iso_8859_6_I, - "ISO-8859-8-E" => Charset_::Iso_8859_8_E, - "ISO-8859-8-I" => Charset_::Iso_8859_8_I, - "GB2312" => Charset_::Gb2312, - "5" => Charset_::Big5, - "KOI8-R" => Charset_::Koi8_R, - _unknown => return Err(CharsetFromStrError(())), - })) - } -} - -#[test] -fn test_parse() { - assert_eq!(Charset::US_ASCII,"us-ascii".parse().unwrap()); - assert_eq!(Charset::US_ASCII,"US-Ascii".parse().unwrap()); - assert_eq!(Charset::US_ASCII,"US-ASCII".parse().unwrap()); - assert_eq!(Charset::SHIFT_JIS,"Shift-JIS".parse().unwrap()); - assert!("abcd".parse(::().is_err()); -} - -#[test] -fn test_display() { - assert_eq!("US-ASCII", format!("{}", Charset::US_ASCII)); -} diff --git a/src/disabled/util/encoding.rs b/src/disabled/util/encoding.rs deleted file mode 100644 index fc972dd3..00000000 --- a/src/disabled/util/encoding.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::fmt; -use std::str; - -pub use self::Encoding::{Chunked, Brotli, Gzip, Deflate, Compress, Identity, EncodingExt, Trailers}; - -/// A value to represent an encoding used in `Transfer-Encoding` -/// or `Accept-Encoding` header. -#[derive(Clone, PartialEq, Debug)] -pub enum Encoding { - /// The `chunked` encoding. - Chunked, - /// The `br` encoding. - Brotli, - /// The `gzip` encoding. - Gzip, - /// The `deflate` encoding. - Deflate, - /// The `compress` encoding. - Compress, - /// The `identity` encoding. - Identity, - /// The `trailers` encoding. - Trailers, - /// Some other encoding that is less common, can be any String. - EncodingExt(String) -} - -impl fmt::Display for Encoding { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - Chunked => "chunked", - Brotli => "br", - Gzip => "gzip", - Deflate => "deflate", - Compress => "compress", - Identity => "identity", - Trailers => "trailers", - EncodingExt(ref s) => s.as_ref() - }) - } -} - -impl str::FromStr for Encoding { - type Err = ::Error; - fn from_str(s: &str) -> ::Result { - match s { - "chunked" => Ok(Chunked), - "br" => Ok(Brotli), - "deflate" => Ok(Deflate), - "gzip" => Ok(Gzip), - "compress" => Ok(Compress), - "identity" => Ok(Identity), - "trailers" => Ok(Trailers), - _ => Ok(EncodingExt(s.to_owned())) - } - } -} diff --git a/src/disabled/util/extended_value.rs b/src/disabled/util/extended_value.rs deleted file mode 100644 index 0098f627..00000000 --- a/src/disabled/util/extended_value.rs +++ /dev/null @@ -1,192 +0,0 @@ -/// An extended header parameter value (i.e., tagged with a character set and optionally, -/// a language), as defined in [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2). -#[derive(Clone, Debug, PartialEq)] -pub struct ExtendedValue { - /// The character set that is used to encode the `value` to a string. - pub charset: Charset, - /// The human language details of the `value`, if available. - pub language_tag: Option, - /// The parameter value, as expressed in octets. - pub value: Vec, -} - -/// Parses extended header parameter values (`ext-value`), as defined in -/// [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2). -/// -/// Extended values are denoted by parameter names that end with `*`. -/// -/// ## ABNF -/// -/// ```text -/// ext-value = charset "'" [ language ] "'" value-chars -/// ; like RFC 2231's -/// ; (see [RFC2231], Section 7) -/// -/// charset = "UTF-8" / "ISO-8859-1" / mime-charset -/// -/// mime-charset = 1*mime-charsetc -/// mime-charsetc = ALPHA / DIGIT -/// / "!" / "#" / "$" / "%" / "&" -/// / "+" / "-" / "^" / "_" / "`" -/// / "{" / "}" / "~" -/// ; as in Section 2.3 of [RFC2978] -/// ; except that the single quote is not included -/// ; SHOULD be registered in the IANA charset registry -/// -/// language = -/// -/// value-chars = *( pct-encoded / attr-char ) -/// -/// pct-encoded = "%" HEXDIG HEXDIG -/// ; see [RFC3986], Section 2.1 -/// -/// attr-char = ALPHA / DIGIT -/// / "!" / "#" / "$" / "&" / "+" / "-" / "." -/// / "^" / "_" / "`" / "|" / "~" -/// ; token except ( "*" / "'" / "%" ) -/// ``` -pub fn parse_extended_value(val: &str) -> ::Result { - - // Break into three pieces separated by the single-quote character - let mut parts = val.splitn(3,'\''); - - // Interpret the first piece as a Charset - let charset: Charset = match parts.next() { - None => return Err(::Error::Header), - Some(n) => try!(FromStr::from_str(n)), - }; - - // Interpret the second piece as a language tag - let lang: Option = match parts.next() { - None => return Err(::Error::Header), - Some("") => None, - Some(s) => match s.parse() { - Ok(lt) => Some(lt), - Err(_) => return Err(::Error::Header), - } - }; - - // Interpret the third piece as a sequence of value characters - let value: Vec = match parts.next() { - None => return Err(::Error::Header), - Some(v) => percent_encoding::percent_decode(v.as_bytes()).collect(), - }; - - Ok(ExtendedValue { - charset: charset, - language_tag: lang, - value: value, - }) -} - - -impl Display for ExtendedValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let encoded_value = - percent_encoding::percent_encode(&self.value[..], self::percent_encoding_http::HTTP_VALUE); - if let Some(ref lang) = self.language_tag { - write!(f, "{}'{}'{}", self.charset, lang, encoded_value) - } else { - write!(f, "{}''{}", self.charset, encoded_value) - } - } -} - -/// Percent encode a sequence of bytes with a character set defined in -/// [https://tools.ietf.org/html/rfc5987#section-3.2][url] -/// -/// [url]: https://tools.ietf.org/html/rfc5987#section-3.2 -pub fn http_percent_encode(f: &mut fmt::Formatter, bytes: &[u8]) -> fmt::Result { - let encoded = percent_encoding::percent_encode(bytes, self::percent_encoding_http::HTTP_VALUE); - fmt::Display::fmt(&encoded, f) -} - -mod percent_encoding_http { - use percent_encoding; - - // internal module because macro is hard-coded to make a public item - // but we don't want to public export this item - define_encode_set! { - // This encode set is used for HTTP header values and is defined at - // https://tools.ietf.org/html/rfc5987#section-3.2 - pub HTTP_VALUE = [percent_encoding::SIMPLE_ENCODE_SET] | { - ' ', '"', '%', '\'', '(', ')', '*', ',', '/', ':', ';', '<', '-', '>', '?', - '[', '\\', ']', '{', '}' - } - } -} - -#[cfg(test)] -mod tests { - use shared::Charset; - use super::{ExtendedValue, parse_extended_value}; - use language_tags::LanguageTag; - - #[test] - fn test_parse_extended_value_with_encoding_and_language_tag() { - let expected_language_tag = "en".parse::().unwrap(); - // RFC 5987, Section 3.2.2 - // Extended notation, using the Unicode character U+00A3 (POUND SIGN) - let result = parse_extended_value("iso-8859-1'en'%A3%20rates"); - assert!(result.is_ok()); - let extended_value = result.unwrap(); - assert_eq!(Charset::Iso_8859_1, extended_value.charset); - assert!(extended_value.language_tag.is_some()); - assert_eq!(expected_language_tag, extended_value.language_tag.unwrap()); - assert_eq!(vec![163, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value); - } - - #[test] - fn test_parse_extended_value_with_encoding() { - // RFC 5987, Section 3.2.2 - // Extended notation, using the Unicode characters U+00A3 (POUND SIGN) - // and U+20AC (EURO SIGN) - let result = parse_extended_value("UTF-8''%c2%a3%20and%20%e2%82%ac%20rates"); - assert!(result.is_ok()); - let extended_value = result.unwrap(); - assert_eq!(Charset::Ext("UTF-8".to_string()), extended_value.charset); - assert!(extended_value.language_tag.is_none()); - assert_eq!(vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value); - } - - #[test] - fn test_parse_extended_value_missing_language_tag_and_encoding() { - // From: https://greenbytes.de/tech/tc2231/#attwithfn2231quot2 - let result = parse_extended_value("foo%20bar.html"); - assert!(result.is_err()); - } - - #[test] - fn test_parse_extended_value_partially_formatted() { - let result = parse_extended_value("UTF-8'missing third part"); - assert!(result.is_err()); - } - - #[test] - fn test_parse_extended_value_partially_formatted_blank() { - let result = parse_extended_value("blank second part'"); - assert!(result.is_err()); - } - - #[test] - fn test_fmt_extended_value_with_encoding_and_language_tag() { - let extended_value = ExtendedValue { - charset: Charset::Iso_8859_1, - language_tag: Some("en".parse().expect("Could not parse language tag")), - value: vec![163, b' ', b'r', b'a', b't', b'e', b's'], - }; - assert_eq!("ISO-8859-1'en'%A3%20rates", format!("{}", extended_value)); - } - - #[test] - fn test_fmt_extended_value_with_encoding() { - let extended_value = ExtendedValue { - charset: Charset::Ext("UTF-8".to_string()), - language_tag: None, - value: vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', - b't', b'e', b's'], - }; - assert_eq!("UTF-8''%C2%A3%20and%20%E2%82%AC%20rates", - format!("{}", extended_value)); - } -} diff --git a/src/disabled/util/quality_value.rs b/src/disabled/util/quality_value.rs deleted file mode 100644 index bcc79728..00000000 --- a/src/disabled/util/quality_value.rs +++ /dev/null @@ -1,268 +0,0 @@ -#[allow(unused, deprecated)] -use std::ascii::AsciiExt; -use std::cmp; -use std::default::Default; -use std::fmt; -use std::str; - -#[cfg(test)] -use self::internal::IntoQuality; - -/// Represents a quality used in quality values. -/// -/// Can be created with the `q` function. -/// -/// # Implementation notes -/// -/// The quality value is defined as a number between 0 and 1 with three decimal places. This means -/// there are 1001 possible values. Since floating point numbers are not exact and the smallest -/// floating point data type (`f32`) consumes four bytes, hyper uses an `u16` value to store the -/// quality internally. For performance reasons you may set quality directly to a value between -/// 0 and 1000 e.g. `Quality(532)` matches the quality `q=0.532`. -/// -/// [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. -#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Quality(u16); - -impl Default for Quality { - fn default() -> Quality { - Quality(1000) - } -} - -/// Represents an item with a quality value as defined in -/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.1). -#[derive(Clone, PartialEq, Debug)] -pub struct QualityValue { - /// The actual contents of the field. - value: T, - /// The quality (client or server preference) for the value. - quality: Quality, -} - -impl QualityValue { - /// Creates a new `QualityValue` from an item and a quality. - pub fn new(value: T, quality: Quality) -> QualityValue { - QualityValue { - value, - quality, - } - } - - /* - /// Convenience function to set a `Quality` from a float or integer. - /// - /// Implemented for `u16` and `f32`. - /// - /// # Panic - /// - /// Panics if value is out of range. - pub fn with_q(mut self, q: Q) -> QualityValue { - self.quality = q.into_quality(); - self - } - */ -} - -impl From for QualityValue { - fn from(value: T) -> QualityValue { - QualityValue { - value, - quality: Quality::default(), - } - } -} - -impl cmp::PartialOrd for QualityValue { - fn partial_cmp(&self, other: &QualityValue) -> Option { - self.quality.partial_cmp(&other.quality) - } -} - -impl fmt::Display for QualityValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&self.value, f)?; - match self.quality.0 { - 1000 => Ok(()), - 0 => f.write_str("; q=0"), - x => write!(f, "; q=0.{}", format!("{:03}", x).trim_right_matches('0')) - } - } -} - -impl str::FromStr for QualityValue { - type Err = ::Error; - fn from_str(s: &str) -> ::Result> { - // Set defaults used if parsing fails. - let mut raw_item = s; - let mut quality = 1f32; - - let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect(); - if parts.len() == 2 { - if parts[0].len() < 2 { - return Err(::Error::invalid()); - } - if parts[0].starts_with("q=") || parts[0].starts_with("Q=") { - let q_part = &parts[0][2..parts[0].len()]; - if q_part.len() > 5 { - return Err(::Error::invalid()); - } - match q_part.parse::() { - Ok(q_value) => { - if 0f32 <= q_value && q_value <= 1f32 { - quality = q_value; - raw_item = parts[1]; - } else { - return Err(::Error::invalid()); - } - }, - Err(_) => { - return Err(::Error::invalid()) - }, - } - } - } - match raw_item.parse::() { - // we already checked above that the quality is within range - Ok(item) => Ok(QualityValue::new(item, from_f32(quality))), - Err(_) => { - Err(::Error::invalid()) - }, - } - } -} - -#[inline] -fn from_f32(f: f32) -> Quality { - // this function is only used internally. A check that `f` is within range - // should be done before calling this method. Just in case, this - // debug_assert should catch if we were forgetful - debug_assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0"); - Quality((f * 1000f32) as u16) -} - -#[cfg(test)] -fn q(val: T) -> Quality { - val.into_quality() -} - -mod internal { - use super::Quality; - - // TryFrom is probably better, but it's not stable. For now, we want to - // keep the functionality of the `q` function, while allowing it to be - // generic over `f32` and `u16`. - // - // `q` would panic before, so keep that behavior. `TryFrom` can be - // introduced later for a non-panicking conversion. - - pub trait IntoQuality: Sealed + Sized { - fn into_quality(self) -> Quality; - } - - impl IntoQuality for f32 { - fn into_quality(self) -> Quality { - assert!(self >= 0f32 && self <= 1f32, "float must be between 0.0 and 1.0"); - super::from_f32(self) - } - } - - impl IntoQuality for u16 { - fn into_quality(self) -> Quality { - assert!(self <= 1000, "u16 must be between 0 and 1000"); - Quality(self) - } - } - - - pub trait Sealed {} - impl Sealed for u16 {} - impl Sealed for f32 {} -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_quality_item_fmt_q_1() { - let x = QualityValue::from("foo"); - assert_eq!(format!("{}", x), "foo"); - } - #[test] - fn test_quality_item_fmt_q_0001() { - let x = QualityValue::new("foo", Quality(1)); - assert_eq!(format!("{}", x), "foo; q=0.001"); - } - #[test] - fn test_quality_item_fmt_q_05() { - let x = QualityValue::new("foo", Quality(500)); - assert_eq!(format!("{}", x), "foo; q=0.5"); - } - - #[test] - fn test_quality_item_fmt_q_0() { - let x = QualityValue::new("foo", Quality(0)); - assert_eq!(x.to_string(), "foo; q=0"); - } - - #[test] - fn test_quality_item_from_str1() { - let x: QualityValue = "chunked".parse().unwrap(); - assert_eq!(x, QualityValue { value: "chunked".to_owned(), quality: Quality(1000), }); - } - #[test] - fn test_quality_item_from_str2() { - let x: QualityValue = "chunked; q=1".parse().unwrap(); - assert_eq!(x, QualityValue { value: "chunked".to_owned(), quality: Quality(1000), }); - } - #[test] - fn test_quality_item_from_str3() { - let x: QualityValue = "gzip; q=0.5".parse().unwrap(); - assert_eq!(x, QualityValue { value: "gzip".to_owned(), quality: Quality(500), }); - } - #[test] - fn test_quality_item_from_str4() { - let x: QualityValue = "gzip; q=0.273".parse().unwrap(); - assert_eq!(x, QualityValue { value: "gzip".to_owned(), quality: Quality(273), }); - } - #[test] - fn test_quality_item_from_str5() { - assert!("gzip; q=0.2739999".parse::>().is_err()); - } - - #[test] - fn test_quality_item_from_str6() { - assert!("gzip; q=2".parse::>().is_err()); - } - #[test] - fn test_quality_item_ordering() { - let x: QualityValue = "gzip; q=0.5".parse().unwrap(); - let y: QualityValue = "gzip; q=0.273".parse().unwrap(); - assert!(x > y) - } - - #[test] - fn test_quality() { - assert_eq!(q(0.5), Quality(500)); - } - - #[test] - #[should_panic] - fn test_quality_invalid() { - q(-1.0); - } - - #[test] - #[should_panic] - fn test_quality_invalid2() { - q(2.0); - } - - #[test] - fn test_fuzzing_bugs() { - assert!("99999;".parse::>().is_err()); - assert!("\x0d;;;=\u{d6aa}==".parse::>().is_ok()) - } -} diff --git a/src/disabled/warning.rs b/src/disabled/warning.rs deleted file mode 100644 index 92272015..00000000 --- a/src/disabled/warning.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::fmt; -use std::str::{FromStr}; -use {Header, HttpDate, Raw}; -use parsing::from_one_raw_str; - -/// `Warning` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.5) -/// -/// The `Warning` header field can be be used to carry additional information -/// about the status or transformation of a message that might not be reflected -/// in the status code. This header is sometimes used as backwards -/// compatible way to notify of a deprecated API. -/// -/// # ABNF -/// -/// ```text -/// Warning = 1#warning-value -/// warning-value = warn-code SP warn-agent SP warn-text -/// [ SP warn-date ] -/// warn-code = 3DIGIT -/// warn-agent = ( uri-host [ ":" port ] ) / pseudonym -/// ; the name or pseudonym of the server adding -/// ; the Warning header field, for use in debugging -/// ; a single "-" is recommended when agent unknown -/// warn-text = quoted-string -/// warn-date = DQUOTE HTTP-date DQUOTE -/// ``` -/// -/// # Example values -/// -/// * `Warning: 112 - "network down" "Sat, 25 Aug 2012 23:34:45 GMT"` -/// * `Warning: 299 - "Deprecated API " "Tue, 15 Nov 1994 08:12:31 GMT"` -/// * `Warning: 299 api.hyper.rs:8080 "Deprecated API : use newapi.hyper.rs instead."` -/// * `Warning: 299 api.hyper.rs:8080 "Deprecated API : use newapi.hyper.rs instead." "Tue, 15 Nov 1994 08:12:31 GMT"` -/// -/// # Examples -/// -/// ``` -/// use headers::{Headers, Warning}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// Warning{ -/// code: 299, -/// agent: "api.hyper.rs".to_owned(), -/// text: "Deprecated".to_owned(), -/// date: None -/// } -/// ); -/// ``` -/// -/// ``` -/// use headers::{Headers, HttpDate, Warning}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// Warning{ -/// code: 299, -/// agent: "api.hyper.rs".to_owned(), -/// text: "Deprecated".to_owned(), -/// date: "Tue, 15 Nov 1994 08:12:31 GMT".parse::().ok() -/// } -/// ); -/// ``` -/// -/// ``` -/// use std::time::SystemTime; -/// use headers::{Headers, Warning}; -/// -/// let mut headers = Headers::new(); -/// headers.set( -/// Warning{ -/// code: 199, -/// agent: "api.hyper.rs".to_owned(), -/// text: "Deprecated".to_owned(), -/// date: Some(SystemTime::now().into()) -/// } -/// ); -/// ``` -#[derive(PartialEq, Clone, Debug)] -pub struct Warning { - /// The 3 digit warn code. - pub code: u16, - /// The name or pseudonym of the server adding this header. - pub agent: String, - /// The warning message describing the error. - pub text: String, - /// An optional warning date. - pub date: Option -} - -impl Header for Warning { - fn header_name() -> &'static str { - static NAME: &'static str = "Warning"; - NAME - } - - fn parse_header(raw: &Raw) -> ::Result { - from_one_raw_str(raw) - } - - fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result { - f.fmt_line(self) - } -} - -impl fmt::Display for Warning { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.date { - Some(date) => write!(f, "{:03} {} \"{}\" \"{}\"", self.code, self.agent, self.text, date), - None => write!(f, "{:03} {} \"{}\"", self.code, self.agent, self.text) - } - } -} - -impl FromStr for Warning { - type Err = ::Error; - - fn from_str(s: &str) -> ::Result { - let mut warning_split = s.split_whitespace(); - let code = match warning_split.next() { - Some(c) => match c.parse::() { - Ok(c) => c, - Err(..) => return Err(::Error::Header) - }, - None => return Err(::Error::Header) - }; - let agent = match warning_split.next() { - Some(a) => a.to_string(), - None => return Err(::Error::Header) - }; - - let mut warning_split = s.split('"').skip(1); - let text = match warning_split.next() { - Some(t) => t.to_string(), - None => return Err(::Error::Header) - }; - let date = match warning_split.skip(1).next() { - Some(d) => d.parse::().ok(), - None => None // Optional - }; - - Ok(Warning { - code: code, - agent: agent, - text: text, - date: date - }) - } -} - -#[cfg(test)] -mod tests { - use super::Warning; - use {Header, HttpDate}; - - #[test] - fn test_parsing() { - let warning = Header::parse_header(&vec![b"112 - \"network down\" \"Sat, 25 Aug 2012 23:34:45 GMT\"".to_vec()].into()); - assert_eq!(warning.ok(), Some(Warning { - code: 112, - agent: "-".to_owned(), - text: "network down".to_owned(), - date: "Sat, 25 Aug 2012 23:34:45 GMT".parse::().ok() - })); - - let warning = Header::parse_header(&vec![b"299 api.hyper.rs:8080 \"Deprecated API : use newapi.hyper.rs instead.\"".to_vec()].into()); - assert_eq!(warning.ok(), Some(Warning { - code: 299, - agent: "api.hyper.rs:8080".to_owned(), - text: "Deprecated API : use newapi.hyper.rs instead.".to_owned(), - date: None - })); - - let warning = Header::parse_header(&vec![b"299 api.hyper.rs:8080 \"Deprecated API : use newapi.hyper.rs instead.\" \"Tue, 15 Nov 1994 08:12:31 GMT\"".to_vec()].into()); - assert_eq!(warning.ok(), Some(Warning { - code: 299, - agent: "api.hyper.rs:8080".to_owned(), - text: "Deprecated API : use newapi.hyper.rs instead.".to_owned(), - date: "Tue, 15 Nov 1994 08:12:31 GMT".parse::().ok() - })); - } -}