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