Skip to content

Commit 0879ada

Browse files
committed
flushing out impl of Accept-Encoding header, adding convience methods
1 parent 2d14c19 commit 0879ada

File tree

2 files changed

+147
-8
lines changed

2 files changed

+147
-8
lines changed

src/common/accept_encoding.rs

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,134 @@
1-
use util::QualityValue;
2-
// use HeaderValue;
3-
1+
use std::convert::TryFrom;
2+
use util::{QualityValue, TryFromValues};
3+
use HeaderValue;
44

55
/// `Accept-Encoding` header, defined in
66
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.4)
7-
///
7+
///
8+
/// The `Accept-Encoding` header field can be used by user agents to
9+
/// indicate what response content-codings are acceptable in the response.
10+
/// An "identity" token is used as a synonym for "no encoding" in
11+
/// order to communicate when no encoding is preferred.
12+
///
13+
/// # ABNF
14+
///
15+
/// ```text
16+
/// Accept-Encoding = #( codings [ weight ] )
17+
/// codings = content-coding / "identity" / "*"
18+
/// ```
19+
///
20+
/// # Example Values
21+
///
22+
/// * `gzip`
23+
/// * `br;q=1.0, gzip;q=0.8`
24+
///
825
#[derive(Clone, Debug)]
926
pub struct AcceptEncoding(QualityValue);
1027

1128
derive_header! {
1229
AcceptEncoding(_),
1330
name: ACCEPT_ENCODING
1431
}
32+
33+
impl AcceptEncoding {
34+
/// Convience method
35+
#[inline]
36+
pub fn gzip() -> AcceptEncoding {
37+
AcceptEncoding(HeaderValue::from_static("gzip").into())
38+
}
39+
40+
/// A convience method to create an Accept-Encoding header from pairs of values and qualities
41+
///
42+
/// # Example
43+
///
44+
/// ```
45+
/// use headers::AcceptEncoding;
46+
///
47+
/// let pairs = vec![("gzip", 1.0), ("deflate", 0.8)];
48+
/// let header = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter());
49+
/// ```
50+
pub fn from_quality_pairs<'i, I>(pairs: &mut I) -> Result<AcceptEncoding, ::Error>
51+
where
52+
I: Iterator<Item = (&'i str, f32)>,
53+
{
54+
let values: Vec<HeaderValue> = pairs
55+
.map(|pair| {
56+
QualityValue::try_from(pair).map(|qual: QualityValue| HeaderValue::from(qual))
57+
})
58+
.collect::<Result<Vec<HeaderValue>, ::Error>>()?;
59+
let value = QualityValue::try_from_values(&mut values.iter())?;
60+
Ok(AcceptEncoding(value))
61+
}
62+
63+
/// Returns the most prefered encoding that is specified by the client,
64+
/// if one is specified.
65+
///
66+
/// Note: This peeks at the underlying iter, not modifying it.
67+
///
68+
/// # Example
69+
///
70+
/// ```
71+
/// use headers::AcceptEncoding;
72+
///
73+
/// let pairs = vec![("gzip", 1.0), ("deflate", 0.8)];
74+
/// let accept_enc = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter()).unwrap();
75+
/// let mut encodings = accept_enc.sorted_encodings();
76+
///
77+
/// assert_eq!(accept_enc.prefered_encoding(), Some("gzip"));
78+
/// ```
79+
pub fn prefered_encoding(&self) -> Option<&str> {
80+
self.0.iter().peekable().peek().map(|s| *s)
81+
}
82+
83+
/// Returns a quality sorted iterator of the accepted encodings
84+
///
85+
/// # Example
86+
///
87+
/// ```
88+
/// use headers::{AcceptEncoding, HeaderValue};
89+
///
90+
/// let pairs = vec![("gzip", 1.0), ("deflate", 0.8)];
91+
/// let accept_enc = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter()).unwrap();
92+
/// let mut encodings = accept_enc.sorted_encodings();
93+
///
94+
/// assert_eq!(encodings.next(), Some("gzip"));
95+
/// assert_eq!(encodings.next(), Some("deflate"));
96+
/// assert_eq!(encodings.next(), None);
97+
/// ```
98+
pub fn sorted_encodings(&self) -> impl Iterator<Item = &str> {
99+
self.0.iter()
100+
}
101+
}
102+
103+
#[cfg(test)]
104+
mod tests {
105+
use super::*;
106+
use HeaderValue;
107+
108+
#[test]
109+
fn from_static() {
110+
let val = HeaderValue::from_static("deflate, gzip;q=1.0, br;q=0.9");
111+
let accept_enc = AcceptEncoding(val.into());
112+
113+
assert_eq!(accept_enc.prefered_encoding(), Some("deflate"));
114+
115+
let mut encodings = accept_enc.sorted_encodings();
116+
assert_eq!(encodings.next(), Some("deflate"));
117+
assert_eq!(encodings.next(), Some("gzip"));
118+
assert_eq!(encodings.next(), Some("br"));
119+
assert_eq!(encodings.next(), None);
120+
}
121+
122+
#[test]
123+
fn from_pairs() {
124+
let pairs = vec![("gzip", 1.0), ("br", 0.9)];
125+
let accept_enc = AcceptEncoding::from_quality_pairs(&mut pairs.into_iter()).unwrap();
126+
127+
assert_eq!(accept_enc.prefered_encoding(), Some("gzip"));
128+
129+
let mut encodings = accept_enc.sorted_encodings();
130+
assert_eq!(encodings.next(), Some("gzip"));
131+
assert_eq!(encodings.next(), Some("br"));
132+
assert_eq!(encodings.next(), None);
133+
}
134+
}

src/util/quality_value.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::cmp::Ordering;
2-
use std::convert::TryFrom;
2+
use std::convert::{From, TryFrom};
33
use std::marker::PhantomData;
44

5-
use HeaderValue;
65
use itertools::Itertools;
76
use util::{FlatCsv, TryFromValues};
7+
use HeaderValue;
88

99
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1010
pub(crate) struct QualityValue<QualSep = SemiQ> {
@@ -100,6 +100,19 @@ impl<Delm: QualityDelimiter> From<FlatCsv> for QualityValue<Delm> {
100100
}
101101
}
102102

103+
impl<Delm: QualityDelimiter, F: Into<f32>> TryFrom<(&str, F)> for QualityValue<Delm> {
104+
type Error = ::Error;
105+
106+
fn try_from(pair: (&str, F)) -> Result<Self, ::Error> {
107+
let value = HeaderValue::try_from(format!("{}{}{}", pair.0, Delm::STR, pair.1.into()))
108+
.map_err(|_e| ::Error::invalid())?;
109+
Ok(QualityValue {
110+
csv: value.into(),
111+
_marker: PhantomData,
112+
})
113+
}
114+
}
115+
103116
impl<Delm> From<HeaderValue> for QualityValue<Delm> {
104117
fn from(value: HeaderValue) -> Self {
105118
QualityValue {
@@ -115,9 +128,15 @@ impl<'a, Delm> From<&'a QualityValue<Delm>> for HeaderValue {
115128
}
116129
}
117130

131+
impl<Delm> From<QualityValue<Delm>> for HeaderValue {
132+
fn from(qual: QualityValue<Delm>) -> HeaderValue {
133+
qual.csv.value
134+
}
135+
}
136+
118137
impl<Delm: QualityDelimiter> TryFromValues for QualityValue<Delm> {
119138
fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
120-
where
139+
where
121140
I: Iterator<Item = &'i HeaderValue>,
122141
{
123142
let flat: FlatCsv = values.collect();
@@ -143,7 +162,7 @@ mod tests {
143162

144163
#[test]
145164
fn multiple_qualities_wrong_order() {
146-
let val = HeaderValue::from_static("br;q=0.8, gzip;q=1");
165+
let val = HeaderValue::from_static("br;q=0.8, gzip;q=1.0");
147166
let qual = QualityValue::<SemiQ>::from(val);
148167

149168
let mut values = qual.iter();

0 commit comments

Comments
 (0)