headers_accept_encoding/common/
accept_encoding.rs

1use std::convert::TryFrom;
2
3use util::{QualityValue, TryFromValues};
4use {ContentCoding, HeaderValue};
5
6/// `Accept-Encoding` header, defined in
7/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.4)
8///
9/// The `Accept-Encoding` header field can be used by user agents to
10/// indicate what response content-codings are acceptable in the response.
11/// An "identity" token is used as a synonym for "no encoding" in
12/// order to communicate when no encoding is preferred.
13///
14/// # ABNF
15///
16/// ```text
17/// Accept-Encoding  = #( codings [ weight ] )
18/// codings          = content-coding / "identity" / "*"
19/// ```
20///
21/// # Example Values
22///
23/// * `gzip`
24/// * `br;q=1.0, gzip;q=0.8`
25///
26#[derive(Clone, Debug)]
27pub struct AcceptEncoding(pub QualityValue);
28
29derive_header! {
30    AcceptEncoding(_),
31    name: ACCEPT_ENCODING
32}
33
34impl AcceptEncoding {
35    /// Convience method to create an `Accept-Encoding: gzip` header
36    #[inline]
37    pub fn gzip() -> AcceptEncoding {
38        AcceptEncoding(HeaderValue::from_static("gzip").into())
39    }
40
41    /// A convience method to create an Accept-Encoding header from pairs of values and qualities
42    ///
43    /// # Example
44    ///
45    /// ```
46    /// use headers::AcceptEncoding;
47    ///
48    /// let pairs = vec![("gzip", 1.0), ("deflate", 0.8)];
49    /// let header = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter());
50    /// ```
51    pub fn from_quality_pairs<'i, I>(pairs: &mut I) -> Result<AcceptEncoding, ::Error>
52    where
53        I: Iterator<Item = (&'i str, f32)>,
54    {
55        let values: Vec<HeaderValue> = pairs
56            .map(|pair| {
57                QualityValue::try_from(pair).map(|qual: QualityValue| HeaderValue::from(qual))
58            })
59            .collect::<Result<Vec<HeaderValue>, ::Error>>()?;
60        let value = QualityValue::try_from_values(&mut values.iter())?;
61        Ok(AcceptEncoding(value))
62    }
63
64    /// Returns the most prefered encoding that is specified by the header,
65    /// if one is specified.
66    ///
67    /// Note: This peeks at the underlying iter, not modifying it.
68    ///
69    /// # Example
70    ///
71    /// ```
72    /// use headers::{AcceptEncoding, ContentCoding};
73    ///
74    /// let pairs = vec![("gzip", 1.0), ("deflate", 0.8)];
75    /// let accept_enc = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter()).unwrap();
76    /// let mut encodings = accept_enc.sorted_encodings();
77    ///
78    /// assert_eq!(accept_enc.prefered_encoding(), Some(ContentCoding::GZIP));
79    /// ```
80    pub fn prefered_encoding(&self) -> Option<ContentCoding> {
81        self.0
82            .iter()
83            .peekable()
84            .peek()
85            .map(|s| ContentCoding::from_str(*s))
86    }
87
88    /// Returns a quality sorted iterator of the `ContentCoding`
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// use headers::{AcceptEncoding, ContentCoding, HeaderValue};
94    ///
95    /// let val = HeaderValue::from_static("deflate, gzip;q=1.0, br;q=0.8");
96    /// let accept_enc = AcceptEncoding(val.into());
97    /// let mut encodings = accept_enc.sorted_encodings();
98    ///
99    /// assert_eq!(encodings.next(), Some(ContentCoding::DEFLATE));
100    /// assert_eq!(encodings.next(), Some(ContentCoding::GZIP));
101    /// assert_eq!(encodings.next(), Some(ContentCoding::BROTLI));
102    /// assert_eq!(encodings.next(), None);
103    /// ```
104    pub fn sorted_encodings<'a>(&'a self) -> impl Iterator<Item = ContentCoding> + 'a {
105        self.0.iter().map(|s| ContentCoding::from_str(s))
106    }
107
108    /// Returns a quality sorted iterator of values
109    ///
110    /// # Example
111    ///
112    /// ```
113    /// use headers::{AcceptEncoding, ContentCoding, HeaderValue};
114    ///
115    /// let val = HeaderValue::from_static("deflate, gzip;q=1.0, br;q=0.8");
116    /// let accept_enc = AcceptEncoding(val.into());
117    /// let mut encodings = accept_enc.sorted_values();
118    ///
119    /// assert_eq!(encodings.next(), Some("deflate"));
120    /// assert_eq!(encodings.next(), Some("gzip"));
121    /// assert_eq!(encodings.next(), Some("br"));
122    /// assert_eq!(encodings.next(), None);
123    /// ```
124    pub fn sorted_values(&self) -> impl Iterator<Item = &str> {
125        self.0.iter()
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use {ContentCoding, HeaderValue};
133
134    #[test]
135    fn from_static() {
136        let val = HeaderValue::from_static("deflate, gzip;q=1.0, br;q=0.9");
137        let accept_enc = AcceptEncoding(val.into());
138
139        assert_eq!(accept_enc.prefered_encoding(), Some(ContentCoding::DEFLATE));
140
141        let mut encodings = accept_enc.sorted_encodings();
142        assert_eq!(encodings.next(), Some(ContentCoding::DEFLATE));
143        assert_eq!(encodings.next(), Some(ContentCoding::GZIP));
144        assert_eq!(encodings.next(), Some(ContentCoding::BROTLI));
145        assert_eq!(encodings.next(), None);
146    }
147
148    #[test]
149    fn from_pairs() {
150        let pairs = vec![("gzip", 1.0), ("br", 0.9)];
151        let accept_enc = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter()).unwrap();
152
153        assert_eq!(accept_enc.prefered_encoding(), Some(ContentCoding::GZIP));
154
155        let mut encodings = accept_enc.sorted_encodings();
156        assert_eq!(encodings.next(), Some(ContentCoding::GZIP));
157        assert_eq!(encodings.next(), Some(ContentCoding::BROTLI));
158        assert_eq!(encodings.next(), None);
159    }
160}