summaryrefslogtreecommitdiffstats
path: root/third_party/rust/headers/src/disabled/util
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/headers/src/disabled/util')
-rw-r--r--third_party/rust/headers/src/disabled/util/charset.rs229
-rw-r--r--third_party/rust/headers/src/disabled/util/encoding.rs57
-rw-r--r--third_party/rust/headers/src/disabled/util/extended_value.rs192
-rw-r--r--third_party/rust/headers/src/disabled/util/quality_value.rs268
4 files changed, 746 insertions, 0 deletions
diff --git a/third_party/rust/headers/src/disabled/util/charset.rs b/third_party/rust/headers/src/disabled/util/charset.rs
new file mode 100644
index 0000000000..5a3f462829
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/charset.rs
@@ -0,0 +1,229 @@
+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<Self, Self::Err> {
+ 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(::<Charset>().is_err());
+}
+
+#[test]
+fn test_display() {
+ assert_eq!("US-ASCII", format!("{}", Charset::US_ASCII));
+}
diff --git a/third_party/rust/headers/src/disabled/util/encoding.rs b/third_party/rust/headers/src/disabled/util/encoding.rs
new file mode 100644
index 0000000000..fc972dd3c7
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/encoding.rs
@@ -0,0 +1,57 @@
+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<Encoding> {
+ 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/third_party/rust/headers/src/disabled/util/extended_value.rs b/third_party/rust/headers/src/disabled/util/extended_value.rs
new file mode 100644
index 0000000000..0098f6270c
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/extended_value.rs
@@ -0,0 +1,192 @@
+/// 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<LanguageTag>,
+ /// The parameter value, as expressed in octets.
+ pub value: Vec<u8>,
+}
+
+/// 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 <extended-initial-value>
+/// ; (see [RFC2231], Section 7)
+///
+/// charset = "UTF-8" / "ISO-8859-1" / mime-charset
+///
+/// mime-charset = 1*mime-charsetc
+/// mime-charsetc = ALPHA / DIGIT
+/// / "!" / "#" / "$" / "%" / "&"
+/// / "+" / "-" / "^" / "_" / "`"
+/// / "{" / "}" / "~"
+/// ; as <mime-charset> in Section 2.3 of [RFC2978]
+/// ; except that the single quote is not included
+/// ; SHOULD be registered in the IANA charset registry
+///
+/// language = <Language-Tag, defined in [RFC5646], Section 2.1>
+///
+/// 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<ExtendedValue> {
+
+ // 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<LanguageTag> = 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<u8> = 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::<LanguageTag>().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/third_party/rust/headers/src/disabled/util/quality_value.rs b/third_party/rust/headers/src/disabled/util/quality_value.rs
new file mode 100644
index 0000000000..bcc797280d
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/quality_value.rs
@@ -0,0 +1,268 @@
+#[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<T> {
+ /// The actual contents of the field.
+ value: T,
+ /// The quality (client or server preference) for the value.
+ quality: Quality,
+}
+
+impl<T> QualityValue<T> {
+ /// Creates a new `QualityValue` from an item and a quality.
+ pub fn new(value: T, quality: Quality) -> QualityValue<T> {
+ 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<Q: IntoQuality>(mut self, q: Q) -> QualityValue<T> {
+ self.quality = q.into_quality();
+ self
+ }
+ */
+}
+
+impl<T> From<T> for QualityValue<T> {
+ fn from(value: T) -> QualityValue<T> {
+ QualityValue {
+ value,
+ quality: Quality::default(),
+ }
+ }
+}
+
+impl<T: PartialEq> cmp::PartialOrd for QualityValue<T> {
+ fn partial_cmp(&self, other: &QualityValue<T>) -> Option<cmp::Ordering> {
+ self.quality.partial_cmp(&other.quality)
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for QualityValue<T> {
+ 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<T: str::FromStr> str::FromStr for QualityValue<T> {
+ type Err = ::Error;
+ fn from_str(s: &str) -> ::Result<QualityValue<T>> {
+ // 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::<f32>() {
+ 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::<T>() {
+ // 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<T: IntoQuality>(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<String> = "chunked".parse().unwrap();
+ assert_eq!(x, QualityValue { value: "chunked".to_owned(), quality: Quality(1000), });
+ }
+ #[test]
+ fn test_quality_item_from_str2() {
+ let x: QualityValue<String> = "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<String> = "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<String> = "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::<QualityValue<String>>().is_err());
+ }
+
+ #[test]
+ fn test_quality_item_from_str6() {
+ assert!("gzip; q=2".parse::<QualityValue<String>>().is_err());
+ }
+ #[test]
+ fn test_quality_item_ordering() {
+ let x: QualityValue<String> = "gzip; q=0.5".parse().unwrap();
+ let y: QualityValue<String> = "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::<QualityValue<String>>().is_err());
+ assert!("\x0d;;;=\u{d6aa}==".parse::<QualityValue<String>>().is_ok())
+ }
+}