From 725b45de538d32d0335ecf3ca0281be9c9edb940 Mon Sep 17 00:00:00 2001 From: "Charles \"Demurgos\" Samborski" Date: Sat, 22 Jun 2024 11:45:20 +0000 Subject: [PATCH] fix: resolve compilation errors for `link` module The `link` module is currently located in the `./src/disabled` directory. The content of this directory is not mounted as a module in the crate source, so it is not compiled by default. Over time, the code structure, language features and dependencies changed, but the `link` module was not kept up to date and now the module no longer compiles. This commit fixes compilation errors for the existing `link` module. This was achieved by temporarily adding `mod disabled { mod link; }` in the root module and addressing all the errors reported by the compiler. While this commit is part of an overall effort of adding support for `Link` headers, this specific commit restricts its code changes purely to compilation errors. In particular, it does not introduce any change in behavior, and the module remains disabled. Here is the detailed list of changes: - The `try!` macro was replaced with the try operator `?` because `try` is a reserved keyword since Rust 2018. An alternative would have been to use `r#try`, however `?` is more idiomatic in Rust 2018. There are no compat concerns, all Rust versions supported by `headers` support the edition 2018. - Update `Header` impl for `Link` to use the latest function signature. - Rephrase comment in `Header::decode` impl for `Link` (fix typo, improve clarity) - Use standard `Result` type, the code was previously relying on a local type alias for `Result`. - Reformat with Rustfmt - Replace `::Error::Header` with `Error::invalid()` - Remove useless semicolon in `test_link_fmt_delimited` - Replace `Header::parse_header` with `crate::common::test_decode` - Add module level documentation - Update doc tests for `Link` Note: to compile, the code requires adding the dependency `language_tags` version `0.3`. --- src/disabled/link.rs | 329 ++++++++++++++++++++++--------------------- 1 file changed, 167 insertions(+), 162 deletions(-) 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\""); +}