diff --git a/src/disabled/link.rs b/src/disabled/link.rs index 7bf76c2e..f31324d5 100644 --- a/src/disabled/link.rs +++ b/src/disabled/link.rs @@ -1,14 +1,17 @@ -use std::fmt; -use std::borrow::Cow; -use std::str::FromStr; +//! Link header and types. + #[allow(unused, deprecated)] use std::ascii::AsciiExt; +use std::borrow::Cow; +use std::fmt; +use std::str::FromStr; -use mime::Mime; +use http::HeaderValue; use language_tags::LanguageTag; +use mime::Mime; -use parsing; -use {Header, Raw}; +use crate::util; +use crate::{Error, Header}; /// The `Link` header, defined in /// [RFC5988](https://datatracker.ietf.org/doc/html/rfc5988#section-5) @@ -58,21 +61,22 @@ use {Header, Raw}; /// # Examples /// /// ``` -/// use headers::{Headers, Link, LinkValue, RelationType}; +/// use headers::link::{Link, LinkValue, RelationType}; +/// use headers::{HeaderMap, HeaderMapExt}; /// /// 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( +/// let mut headers = HeaderMap::new(); +/// headers.typed_insert( /// 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 + values: Vec, } /// A single `link-value` of a `Link` header, based on: @@ -134,7 +138,7 @@ pub enum MediaDesc { /// all. All, /// Unrecognized media descriptor extension. - Extension(String) + Extension(String), } /// A Link Relation Type Enum based on: @@ -222,7 +226,7 @@ pub enum RelationType { /// working-copy-of. WorkingCopyOf, /// ext-rel-type. - ExtRelType(String) + ExtRelType(String), } //////////////////////////////////////////////////////////////////////////////// @@ -232,7 +236,9 @@ pub enum RelationType { impl Link { /// Create `Link` from a `Vec`. pub fn new(link_values: Vec) -> Link { - Link { values: link_values } + Link { + values: link_values, + } } /// Get the `Link` header's `LinkValue`s. @@ -249,7 +255,9 @@ impl Link { impl LinkValue { /// Create `LinkValue` from URI-Reference. pub fn new(uri: T) -> LinkValue - where T: Into> { + where + T: Into>, + { LinkValue { link: uri.into(), rel: None, @@ -386,34 +394,37 @@ impl LinkValue { //////////////////////////////////////////////////////////////////////////////// impl Header for Link { - fn header_name() -> &'static str { - static NAME: &'static str = "Link"; - NAME + fn name() -> &'static ::http::header::HeaderName { + &::http::header::LINK } - 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) { + fn decode<'i, I: Iterator>(values: &mut I) -> Result { + // If multiple `Link` headers are present in a request, they are all + // parsed into a single Rust value combining all the `link-value` + // entries of those `Link` headers. + + values + .map(|value| { + value + .to_str() + .map_err(|_| Error::invalid()) + .and_then(|s| Link::from_str(s)) + }) + .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)) + } + _ => Some(Err(Error::invalid())), + }) + .ok_or_else(Error::invalid)? } - fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result { - f.fmt_line(self) + fn encode>(&self, values: &mut E) { + values.extend(::std::iter::once(util::fmt(self))); } } @@ -425,33 +436,33 @@ impl fmt::Display for Link { impl fmt::Display for LinkValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "<{}>", self.link)); + write!(f, "<{}>", self.link)?; if let Some(ref rel) = self.rel { - try!(fmt_delimited(f, rel.as_slice(), " ", ("; rel=\"", "\""))); + fmt_delimited(f, rel.as_slice(), " ", ("; rel=\"", "\""))?; } if let Some(ref anchor) = self.anchor { - try!(write!(f, "; anchor=\"{}\"", anchor)); + write!(f, "; anchor=\"{}\"", anchor)?; } if let Some(ref rev) = self.rev { - try!(fmt_delimited(f, rev.as_slice(), " ", ("; rev=\"", "\""))); + 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)); + write!(f, "; hreflang={}", tag)?; } } if let Some(ref media_desc) = self.media_desc { - try!(fmt_delimited(f, media_desc.as_slice(), ", ", ("; media=\"", "\""))); + fmt_delimited(f, media_desc.as_slice(), ", ", ("; media=\"", "\""))?; } if let Some(ref title) = self.title { - try!(write!(f, "; title=\"{}\"", title)); + write!(f, "; title=\"{}\"", title)?; } if let Some(ref title_star) = self.title_star { - try!(write!(f, "; title*={}", title_star)); + write!(f, "; title*={}", title_star)?; } if let Some(ref media_type) = self.media_type { - try!(write!(f, "; type=\"{}\"", media_type)); + write!(f, "; type=\"{}\"", media_type)?; } Ok(()) @@ -459,9 +470,9 @@ impl fmt::Display for LinkValue { } impl FromStr for Link { - type Err = ::Error; + type Err = Error; - fn from_str(s: &str) -> ::Result { + fn from_str(s: &str) -> Result { // Create a split iterator with delimiters: `;`, `,` let link_split = SplitAsciiUnquoted::new(s, ";,"); @@ -473,35 +484,31 @@ impl FromStr for Link { // 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, - } - }, - } - ); + link_values.push(match verify_and_trim(segment.trim(), (b'<', b'>')) { + Err(_) => return Err(Error::invalid()), + 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), + None => return Err(Error::invalid()), Some(p) => p.trim(), }; let link_header = match link_values.last_mut() { - None => return Err(::Error::Header), + None => return Err(Error::invalid()), Some(l) => l, }; @@ -510,24 +517,23 @@ impl FromStr for Link { // 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() - }, + None | Some("") => return Err(Error::invalid()), + Some(s) => s + .trim_matches(|c: char| c == '"' || c.is_whitespace()) + .split(' ') + .map(|t| t.trim().parse()) + .collect::, _>>() + .or_else(|_| Err(Error::invalid())) + .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), + None | Some("") => return Err(Error::invalid()), Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) { - Err(_) => return Err(::Error::Header), + Err(_) => return Err(Error::invalid()), Ok(a) => Some(String::from(a)), }, }; @@ -536,15 +542,14 @@ impl FromStr for Link { // 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() - }, + None | Some("") => return Err(Error::invalid()), + Some(s) => s + .trim_matches(|c: char| c == '"' || c.is_whitespace()) + .split(' ') + .map(|t| t.trim().parse()) + .collect::, _>>() + .or_else(|_| Err(Error::invalid())) + .ok(), } } } else if "hreflang".eq_ignore_ascii_case(link_param_name) { @@ -552,15 +557,13 @@ impl FromStr for Link { // 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, - }, - } - ); + v.push(match link_param_split.next() { + None | Some("") => return Err(Error::invalid()), + Some(s) => match s.trim().parse() { + Err(_) => return Err(Error::invalid()), + Ok(t) => t, + }, + }); link_header.href_lang = Some(v); } else if "media".eq_ignore_ascii_case(link_param_name) { @@ -568,15 +571,14 @@ impl FromStr for Link { // 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() - }, + None | Some("") => return Err(Error::invalid()), + Some(s) => s + .trim_matches(|c: char| c == '"' || c.is_whitespace()) + .split(',') + .map(|t| t.trim().parse()) + .collect::, _>>() + .or_else(|_| Err(Error::invalid())) + .ok(), }; } } else if "title".eq_ignore_ascii_case(link_param_name) { @@ -584,9 +586,9 @@ impl FromStr for Link { // 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), + None | Some("") => return Err(Error::invalid()), Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) { - Err(_) => return Err(::Error::Header), + Err(_) => return Err(Error::invalid()), Ok(t) => Some(String::from(t)), }, }; @@ -599,7 +601,7 @@ impl FromStr for Link { // 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), + None | Some("") => return Err(Error::invalid()), Some(s) => Some(String::from(s.trim())), }; } @@ -608,19 +610,18 @@ impl FromStr for Link { // 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), + None | Some("") => return Err(Error::invalid()), Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) { - Err(_) => return Err(::Error::Header), + Err(_) => return Err(Error::invalid()), Ok(t) => match t.parse() { - Err(_) => return Err(::Error::Header), + Err(_) => return Err(Error::invalid()), Ok(m) => Some(m), }, }, - }; } } else { - return Err(::Error::Header); + return Err(Error::invalid()); } } } @@ -642,14 +643,14 @@ impl fmt::Display for MediaDesc { MediaDesc::Aural => write!(f, "aural"), MediaDesc::All => write!(f, "all"), MediaDesc::Extension(ref other) => write!(f, "{}", other), - } + } } } impl FromStr for MediaDesc { - type Err = ::Error; + type Err = Error; - fn from_str(s: &str) -> ::Result { + fn from_str(s: &str) -> Result { match s { "screen" => Ok(MediaDesc::Screen), "tty" => Ok(MediaDesc::Tty), @@ -660,7 +661,7 @@ impl FromStr for MediaDesc { "braille" => Ok(MediaDesc::Braille), "aural" => Ok(MediaDesc::Aural), "all" => Ok(MediaDesc::All), - _ => Ok(MediaDesc::Extension(String::from(s))), + _ => Ok(MediaDesc::Extension(String::from(s))), } } } @@ -709,14 +710,14 @@ impl fmt::Display for RelationType { 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; + type Err = Error; - fn from_str(s: &str) -> ::Result { + fn from_str(s: &str) -> Result { if "alternate".eq_ignore_ascii_case(s) { Ok(RelationType::Alternate) } else if "appendix".eq_ignore_ascii_case(s) { @@ -810,12 +811,12 @@ impl FromStr for RelationType { struct SplitAsciiUnquoted<'a> { src: &'a str, pos: usize, - del: &'a str + del: &'a str, } impl<'a> SplitAsciiUnquoted<'a> { fn new(s: &'a str, d: &'a str) -> SplitAsciiUnquoted<'a> { - SplitAsciiUnquoted{ + SplitAsciiUnquoted { src: s, pos: 0, del: d, @@ -853,35 +854,38 @@ impl<'a> Iterator for SplitAsciiUnquoted<'a> { } } -fn fmt_delimited(f: &mut fmt::Formatter, p: &[T], d: &str, b: (&str, &str)) -> fmt::Result { +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])); + 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!(f, "{}{}", d, i)?; } // Write a ending string `b.1` before the first element - try!(write!(f, "{}", b.1)); + write!(f, "{}", b.1)?; } Ok(()) } -fn verify_and_trim(s: &str, b: (u8, u8)) -> ::Result<&str> { +fn verify_and_trim(s: &str, b: (u8, u8)) -> Result<&str, Error> { 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()) - ) + Ok(s.trim_matches(|c: char| c == b.0 as char || c == b.1 as char || c.is_whitespace())) } else { - Err(::Error::Header) + Err(Error::invalid()) } } @@ -894,13 +898,9 @@ mod tests { use std::fmt; use std::fmt::Write; - use super::{Link, LinkValue, MediaDesc, RelationType, SplitAsciiUnquoted}; + use super::super::test_decode; use super::{fmt_delimited, verify_and_trim}; - - use Header; - - // use proto::ServerTransaction; - use bytes::BytesMut; + use super::{Link, LinkValue, MediaDesc, RelationType, SplitAsciiUnquoted}; use mime; @@ -911,13 +911,13 @@ mod tests { .push_rev(RelationType::Next) .set_title("previous chapter"); - let link_header = b"; \ + let link_header = "; \ 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)); + let link = test_decode::(&[link_header]); + assert_eq!(link, Some(expected_link)); } #[test] @@ -930,15 +930,15 @@ mod tests { .push_rel(RelationType::Next) .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel"); - let link_header = b"; \ + let link_header = "; \ 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)); + let link = test_decode::(&[link_header]); + assert_eq!(link, Some(expected_link)); } #[test] @@ -953,7 +953,7 @@ mod tests { .set_title_star("title* unparsed") .set_media_type(mime::TEXT_PLAIN); - let link_header = b"; \ + let link_header = "; \ rel=\"previous\"; anchor=\"../anchor/example/\"; \ rev=\"next\"; hreflang=de; media=\"screen\"; \ title=\"previous chapter\"; title*=title* unparsed; \ @@ -961,8 +961,8 @@ mod tests { 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)); + let link = test_decode::(&[link_header]); + assert_eq!(link, Some(expected_link)); } // TODO @@ -1029,36 +1029,36 @@ mod tests { #[test] fn test_link_parsing_errors() { - let link_a = b"http://example.com/TheBook/chapter2; \ + let link_a = "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 mut err: Option = test_decode::(&[link_a]); + assert_eq!(err, None); - let link_b = b"; \ + let link_b = "; \ =\"previous\"; rev=next; title=\"previous chapter\""; - err = Header::parse_header(&vec![link_b.to_vec()].into()); - assert_eq!(err.is_err(), true); + err = test_decode::(&[link_b]); + assert_eq!(err, None); - let link_c = b"; \ + let link_c = "; \ rel=; rev=next; title=\"previous chapter\""; - err = Header::parse_header(&vec![link_c.to_vec()].into()); - assert_eq!(err.is_err(), true); + err = test_decode::(&[link_c]); + assert_eq!(err, None); - let link_d = b"; \ + let link_d = "; \ rel=\"previous\"; rev=next; title="; - err = Header::parse_header(&vec![link_d.to_vec()].into()); - assert_eq!(err.is_err(), true); + err = test_decode::(&[link_d]); + assert_eq!(err, None); - let link_e = b"; \ + let link_e = "; \ rel=\"previous\"; rev=next; attr=unknown"; - err = Header::parse_header(&vec![link_e.to_vec()].into()); - assert_eq!(err.is_err(), true); - } + err = test_decode::(&[link_e]); + assert_eq!(err, None); + } #[test] fn test_link_split_ascii_unquoted_iterator() { @@ -1074,7 +1074,9 @@ mod tests { #[test] fn test_link_fmt_delimited() { - struct TestFormatterStruct<'a> { v: Vec<&'a str> }; + struct TestFormatterStruct<'a> { + v: Vec<&'a str>, + } impl<'a> fmt::Display for TestFormatterStruct<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1082,7 +1084,9 @@ mod tests { } } - let test_formatter = TestFormatterStruct { v: vec!["first", "second"] }; + let test_formatter = TestFormatterStruct { + v: vec!["first", "second"], + }; let mut string = String::new(); write!(&mut string, "{}", test_formatter).unwrap(); @@ -1100,6 +1104,7 @@ mod tests { 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()] }); + // the `bench_header` macro is defined in `crate::common`, not available in `crate::disabled` + // bench_header!(bench_link, Link, b"; rel=\"previous\"; rev=next; title=\"previous chapter\"; type=\"text/html\"; media=\"screen, tty\""); +}