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}