Skip to content

Commit d8ab6af

Browse files
committed
inital commit, added fields to content_encoding, creating a QualityValue util type, creating a basic accept_encoding header that uses QualityValue
1 parent 2e8c12b commit d8ab6af

File tree

7 files changed

+219
-4
lines changed

7 files changed

+219
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ headers-core = { version = "0.2", path = "./headers-core" }
2222
base64 = "0.12"
2323
bitflags = "1.0"
2424
bytes = "0.5"
25+
itertools = "0.9"
2526
mime = "0.3.14"
2627
sha-1 = "0.8"
2728
time = "0.1.34"

src/common/accept_encoding.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use util::QualityValue;
2+
// use HeaderValue;
3+
4+
5+
/// `Accept-Encoding` header, defined in
6+
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.4)
7+
///
8+
#[derive(Clone, Debug)]
9+
pub struct AcceptEncoding(QualityValue);
10+
11+
derive_header! {
12+
AcceptEncoding(_),
13+
name: ACCEPT_ENCODING
14+
}

src/common/content_encoding.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@ impl ContentEncoding {
4646
ContentEncoding(HeaderValue::from_static("gzip").into())
4747
}
4848

49+
/// A constructor to easily create a `Content-Encoding: compress` header.
50+
#[inline]
51+
pub fn compress() -> ContentEncoding {
52+
ContentEncoding(HeaderValue::from_static("compress").into())
53+
}
54+
55+
/// A constructor to easily create a `Content-Encoding: deflate` header.
56+
#[inline]
57+
pub fn deflate() -> ContentEncoding {
58+
ContentEncoding(HeaderValue::from_static("deflate").into())
59+
}
60+
61+
/// A constructor to easily create a `Content-Encoding: identity` header.
62+
#[inline]
63+
pub fn identity() -> ContentEncoding {
64+
ContentEncoding(HeaderValue::from_static("identity").into())
65+
}
66+
67+
/// A constructor to easily create a `Content-Encoding: br` header.
68+
#[inline]
69+
pub fn br() -> ContentEncoding {
70+
ContentEncoding(HeaderValue::from_static("br").into())
71+
}
72+
4973
/// Check if this header contains a given "coding".
5074
///
5175
/// This can be used with these argument types:

src/common/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! is used, such as `ContentType(pub Mime)`.
88
99
//pub use self::accept_charset::AcceptCharset;
10-
//pub use self::accept_encoding::AcceptEncoding;
10+
pub use self::accept_encoding::AcceptEncoding;
1111
//pub use self::accept_language::AcceptLanguage;
1212
pub use self::accept_ranges::AcceptRanges;
1313
//pub use self::accept::Accept;
@@ -127,7 +127,7 @@ macro_rules! bench_header {
127127

128128
//mod accept;
129129
//mod accept_charset;
130-
//mod accept_encoding;
130+
mod accept_encoding;
131131
//mod accept_language;
132132
mod accept_ranges;
133133
mod access_control_allow_credentials;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ extern crate bitflags;
7878
extern crate bytes;
7979
extern crate headers_core;
8080
extern crate http;
81+
extern crate itertools;
8182
extern crate mime;
8283
extern crate sha1;
8384
#[cfg(all(test, feature = "nightly"))]

src/util/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub(crate) use self::fmt::fmt;
88
pub(crate) use self::http_date::HttpDate;
99
pub(crate) use self::iter::IterExt;
1010
//pub use language_tags::LanguageTag;
11-
//pub use self::quality_value::{Quality, QualityValue};
11+
pub(crate) use self::quality_value::QualityValue;
1212
pub(crate) use self::seconds::Seconds;
1313
pub(crate) use self::value_string::HeaderValueString;
1414

@@ -20,7 +20,7 @@ mod flat_csv;
2020
mod fmt;
2121
mod http_date;
2222
mod iter;
23-
//mod quality_value;
23+
mod quality_value;
2424
mod seconds;
2525
mod value_string;
2626

src/util/quality_value.rs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
use std::cmp::Ordering;
2+
use std::convert::TryFrom;
3+
use std::marker::PhantomData;
4+
5+
use HeaderValue;
6+
use itertools::Itertools;
7+
use util::{FlatCsv, TryFromValues};
8+
9+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
10+
pub(crate) struct QualityValue<QualSep = SemiQ> {
11+
csv: FlatCsv,
12+
_marker: PhantomData<QualSep>,
13+
}
14+
15+
pub(crate) trait QualityDelimiter {
16+
const STR: &'static str;
17+
}
18+
19+
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
20+
pub(crate) enum SemiQ {}
21+
22+
impl QualityDelimiter for SemiQ {
23+
const STR: &'static str = ";q=";
24+
}
25+
26+
#[derive(Clone, Debug, PartialEq, Eq)]
27+
struct QualityMeta<'a, Sep = SemiQ> {
28+
pub data: &'a str,
29+
pub quality: u16,
30+
_marker: PhantomData<Sep>,
31+
}
32+
33+
impl<Delm: QualityDelimiter + Ord> Ord for QualityMeta<'_, Delm> {
34+
fn cmp(&self, other: &Self) -> Ordering {
35+
other.quality.cmp(&self.quality)
36+
}
37+
}
38+
39+
impl<Delm: QualityDelimiter + Ord> PartialOrd for QualityMeta<'_, Delm> {
40+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
41+
Some(self.cmp(other))
42+
}
43+
}
44+
45+
impl<'a, Delm: QualityDelimiter> TryFrom<&'a str> for QualityMeta<'a, Delm> {
46+
type Error = ::Error;
47+
48+
fn try_from(val: &'a str) -> Result<Self, ::Error> {
49+
let mut parts: Vec<&str> = val.split(Delm::STR).collect();
50+
51+
match (parts.pop(), parts.pop()) {
52+
(Some(qual), Some(data)) => {
53+
let parsed: f32 = qual.parse().map_err(|_| ::Error::invalid())?;
54+
let quality = (parsed * 1000_f32) as u16;
55+
56+
Ok(QualityMeta {
57+
data,
58+
quality,
59+
_marker: PhantomData,
60+
})
61+
}
62+
// No deliter present, assign a quality value of 1
63+
(Some(data), None) => Ok(QualityMeta {
64+
data,
65+
quality: 1000_u16,
66+
_marker: PhantomData,
67+
}),
68+
_ => Err(::Error::invalid()),
69+
}
70+
}
71+
}
72+
73+
impl<Delm: QualityDelimiter + Ord> QualityValue<Delm> {
74+
pub(crate) fn iter(&self) -> impl Iterator<Item = &str> {
75+
self.csv
76+
.iter()
77+
.map(|v| QualityMeta::<Delm>::try_from(v).unwrap())
78+
.into_iter()
79+
.sorted()
80+
.map(|pair| pair.data)
81+
.into_iter()
82+
}
83+
}
84+
85+
impl<Delm: QualityDelimiter> From<FlatCsv> for QualityValue<Delm> {
86+
fn from(csv: FlatCsv) -> Self {
87+
QualityValue {
88+
csv,
89+
_marker: PhantomData,
90+
}
91+
}
92+
}
93+
94+
impl<Delm> From<HeaderValue> for QualityValue<Delm> {
95+
fn from(value: HeaderValue) -> Self {
96+
QualityValue {
97+
csv: value.into(),
98+
_marker: PhantomData,
99+
}
100+
}
101+
}
102+
103+
impl<'a, Delm> From<&'a QualityValue<Delm>> for HeaderValue {
104+
fn from(qual: &'a QualityValue<Delm>) -> HeaderValue {
105+
qual.csv.value.clone()
106+
}
107+
}
108+
109+
impl<Delm: QualityDelimiter> TryFromValues for QualityValue<Delm> {
110+
fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
111+
where
112+
I: Iterator<Item = &'i HeaderValue>,
113+
{
114+
let flat: FlatCsv = values.collect();
115+
Ok(QualityValue::from(flat))
116+
}
117+
}
118+
119+
#[cfg(test)]
120+
mod tests {
121+
use super::*;
122+
use util::flat_csv::Comma;
123+
use HeaderValue;
124+
125+
#[test]
126+
fn multiple_qualities() {
127+
let val = HeaderValue::from_static("gzip;q=1, br;q=0.8");
128+
let csv = FlatCsv::<Comma>::from(val);
129+
let qual = QualityValue::<SemiQ>::from(csv);
130+
131+
let mut values = qual.iter();
132+
assert_eq!(values.next(), Some("gzip"));
133+
assert_eq!(values.next(), Some("br"));
134+
assert_eq!(values.next(), None);
135+
}
136+
137+
#[test]
138+
fn multiple_qualities_wrong_order() {
139+
let val = HeaderValue::from_static("br;q=0.8, gzip;q=1");
140+
let csv = FlatCsv::<Comma>::from(val);
141+
let qual = QualityValue::<SemiQ>::from(csv);
142+
143+
let mut values = qual.iter();
144+
assert_eq!(values.next(), Some("gzip"));
145+
assert_eq!(values.next(), Some("br"));
146+
assert_eq!(values.next(), None);
147+
}
148+
149+
#[test]
150+
fn multiple_values() {
151+
let val = HeaderValue::from_static("deflate, gzip;q=1, br;q=0.8");
152+
let csv = FlatCsv::<Comma>::from(val);
153+
let qual = QualityValue::<SemiQ>::from(csv);
154+
155+
let mut values = qual.iter();
156+
assert_eq!(values.next(), Some("deflate"));
157+
assert_eq!(values.next(), Some("gzip"));
158+
assert_eq!(values.next(), Some("br"));
159+
assert_eq!(values.next(), None);
160+
}
161+
162+
#[test]
163+
fn multiple_values_wrong_order() {
164+
let val = HeaderValue::from_static("deflate, br;q=0.8, gzip;q=1, *;q=0.1");
165+
let csv = FlatCsv::<Comma>::from(val);
166+
let qual = QualityValue::<SemiQ>::from(csv);
167+
168+
let mut values = qual.iter();
169+
assert_eq!(values.next(), Some("deflate"));
170+
assert_eq!(values.next(), Some("gzip"));
171+
assert_eq!(values.next(), Some("br"));
172+
assert_eq!(values.next(), Some("*"));
173+
assert_eq!(values.next(), None);
174+
}
175+
}

0 commit comments

Comments
 (0)