Skip to content

Commit 0935725

Browse files
committed
Implement AcceptEncoding
1 parent b243e55 commit 0935725

File tree

3 files changed

+223
-0
lines changed

3 files changed

+223
-0
lines changed

src/content/accept_encoding.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
use crate::content::EncodingProposal;
2+
use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING};
3+
4+
use std::fmt::{self, Debug};
5+
use std::option;
6+
use std::slice;
7+
8+
/// An Accept-Encoding header.
9+
pub struct AcceptEncoding {
10+
entries: Vec<EncodingProposal>,
11+
}
12+
13+
impl AcceptEncoding {
14+
/// Create a new instance of `AcceptEncoding`.
15+
pub fn new() -> Self {
16+
Self { entries: vec![] }
17+
}
18+
19+
/// Create an instance of `AcceptEncoding` from a `Headers` instance.
20+
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
21+
let mut entries = vec![];
22+
let headers = match headers.as_ref().get(ACCEPT_ENCODING) {
23+
Some(headers) => headers,
24+
None => return Ok(None),
25+
};
26+
27+
for value in headers {
28+
for part in value.as_str().trim().split(',') {
29+
// Try and parse a directive from a str. If the directive is
30+
// unkown we skip it.
31+
if let Some(entry) = EncodingProposal::from_str(part)? {
32+
entries.push(entry);
33+
}
34+
}
35+
}
36+
37+
Ok(Some(Self { entries }))
38+
}
39+
40+
/// Push a directive into the list of entries.
41+
pub fn push(&mut self, prop: EncodingProposal) {
42+
self.entries.push(prop);
43+
}
44+
45+
/// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance.
46+
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
47+
headers.as_mut().insert(ACCEPT_ENCODING, self.value());
48+
}
49+
50+
/// Get the `HeaderName`.
51+
pub fn name(&self) -> HeaderName {
52+
ACCEPT_ENCODING
53+
}
54+
55+
/// Get the `HeaderValue`.
56+
pub fn value(&self) -> HeaderValue {
57+
todo!();
58+
}
59+
60+
/// An iterator visiting all entries.
61+
pub fn iter(&self) -> Iter<'_> {
62+
Iter {
63+
inner: self.entries.iter(),
64+
}
65+
}
66+
67+
/// An iterator visiting all entries.
68+
pub fn iter_mut(&mut self) -> IterMut<'_> {
69+
IterMut {
70+
inner: self.entries.iter_mut(),
71+
}
72+
}
73+
}
74+
75+
impl IntoIterator for AcceptEncoding {
76+
type Item = EncodingProposal;
77+
type IntoIter = IntoIter;
78+
79+
#[inline]
80+
fn into_iter(self) -> Self::IntoIter {
81+
IntoIter {
82+
inner: self.entries.into_iter(),
83+
}
84+
}
85+
}
86+
87+
impl<'a> IntoIterator for &'a AcceptEncoding {
88+
type Item = &'a EncodingProposal;
89+
type IntoIter = Iter<'a>;
90+
91+
#[inline]
92+
fn into_iter(self) -> Self::IntoIter {
93+
self.iter()
94+
}
95+
}
96+
97+
impl<'a> IntoIterator for &'a mut AcceptEncoding {
98+
type Item = &'a mut EncodingProposal;
99+
type IntoIter = IterMut<'a>;
100+
101+
#[inline]
102+
fn into_iter(self) -> Self::IntoIter {
103+
self.iter_mut()
104+
}
105+
}
106+
107+
/// A borrowing iterator over entries in `AcceptEncoding`.
108+
#[derive(Debug)]
109+
pub struct IntoIter {
110+
inner: std::vec::IntoIter<EncodingProposal>,
111+
}
112+
113+
impl Iterator for IntoIter {
114+
type Item = EncodingProposal;
115+
116+
fn next(&mut self) -> Option<Self::Item> {
117+
self.inner.next()
118+
}
119+
120+
#[inline]
121+
fn size_hint(&self) -> (usize, Option<usize>) {
122+
self.inner.size_hint()
123+
}
124+
}
125+
126+
/// A lending iterator over entries in `AcceptEncoding`.
127+
#[derive(Debug)]
128+
pub struct Iter<'a> {
129+
inner: slice::Iter<'a, EncodingProposal>,
130+
}
131+
132+
impl<'a> Iterator for Iter<'a> {
133+
type Item = &'a EncodingProposal;
134+
135+
fn next(&mut self) -> Option<Self::Item> {
136+
self.inner.next()
137+
}
138+
139+
#[inline]
140+
fn size_hint(&self) -> (usize, Option<usize>) {
141+
self.inner.size_hint()
142+
}
143+
}
144+
145+
/// A mutable iterator over entries in `AcceptEncoding`.
146+
#[derive(Debug)]
147+
pub struct IterMut<'a> {
148+
inner: slice::IterMut<'a, EncodingProposal>,
149+
}
150+
151+
impl<'a> Iterator for IterMut<'a> {
152+
type Item = &'a mut EncodingProposal;
153+
154+
fn next(&mut self) -> Option<Self::Item> {
155+
self.inner.next()
156+
}
157+
158+
#[inline]
159+
fn size_hint(&self) -> (usize, Option<usize>) {
160+
self.inner.size_hint()
161+
}
162+
}
163+
164+
impl ToHeaderValues for AcceptEncoding {
165+
type Iter = option::IntoIter<HeaderValue>;
166+
fn to_header_values(&self) -> crate::Result<Self::Iter> {
167+
// A HeaderValue will always convert into itself.
168+
Ok(self.value().to_header_values().unwrap())
169+
}
170+
}
171+
172+
impl Debug for AcceptEncoding {
173+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174+
let mut list = f.debug_list();
175+
for directive in &self.entries {
176+
list.entry(directive);
177+
}
178+
list.finish()
179+
}
180+
}

src/content/encoding_proposal.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::content::Encoding;
22
use crate::ensure;
33
use crate::headers::HeaderValue;
4+
use crate::utils::parse_weight;
45

56
use std::cmp::{Ordering, PartialEq};
67

@@ -41,6 +42,24 @@ impl EncodingProposal {
4142
pub fn weight(&self) -> Option<f32> {
4243
self.weight
4344
}
45+
46+
pub(crate) fn from_str(s: &str) -> crate::Result<Option<Self>> {
47+
let s = s.trim();
48+
49+
// We're dealing with an empty string.
50+
if s.is_empty() {
51+
return Ok(None);
52+
}
53+
54+
let mut parts = s.split(';');
55+
let encoding = match Encoding::from_str(parts.next().unwrap()) {
56+
Some(encoding) => encoding,
57+
None => return Ok(None),
58+
};
59+
let weight = parts.next().map(parse_weight).transpose()?;
60+
61+
Ok(Some(Self::new(encoding, weight)?))
62+
}
4463
}
4564

4665
impl From<Encoding> for EncodingProposal {

src/utils/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ mod date;
33
pub(crate) use date::fmt_http_date;
44
pub(crate) use date::parse_http_date;
55

6+
use crate::{Error, Status, StatusCode};
7+
use std::str::FromStr;
8+
69
/// Declares unstable items.
710
#[doc(hidden)]
811
macro_rules! cfg_unstable {
@@ -14,3 +17,24 @@ macro_rules! cfg_unstable {
1417
)*
1518
}
1619
}
20+
21+
/// Parse a weight of the form `q=0.123`.
22+
pub(crate) fn parse_weight(s: &str) -> crate::Result<f32> {
23+
let mut parts = s.split("=");
24+
if !matches!(parts.next(), Some("q")) {
25+
let mut err = Error::new_adhoc("invalid weight");
26+
err.set_status(StatusCode::BadRequest);
27+
return Err(err);
28+
}
29+
match parts.next() {
30+
Some(s) => {
31+
let weight = f32::from_str(s).status(400)?;
32+
Ok(weight)
33+
}
34+
None => {
35+
let mut err = Error::new_adhoc("invalid weight");
36+
err.set_status(StatusCode::BadRequest);
37+
Err(err)
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)