Skip to content

Commit f9c3cff

Browse files
authored
Merge pull request #110 from http-rs/index
Index
2 parents 6dceef3 + 29fc199 commit f9c3cff

File tree

5 files changed

+317
-172
lines changed

5 files changed

+317
-172
lines changed

src/headers/headers.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
//! HTTP headers.
2+
3+
use std::collections::HashMap;
4+
use std::convert::TryInto;
5+
use std::iter::IntoIterator;
6+
use std::ops::Index;
7+
use std::str::FromStr;
8+
9+
use crate::headers::{
10+
HeaderName, HeaderValues, IntoIter, Iter, IterMut, Names, ToHeaderValues, Values,
11+
};
12+
13+
/// A collection of HTTP Headers.
14+
///
15+
/// Headers are never manually constructed, but are part of `Request`,
16+
/// `Response`, and `Trailers`. Each of these types implements `AsRef<Headers>`
17+
/// and `AsMut<Headers>` so functions that want to modify headers can be generic
18+
/// over either of these traits.
19+
///
20+
/// # Examples
21+
///
22+
/// ```
23+
/// use http_types::{Response, StatusCode};
24+
///
25+
/// let mut res = Response::new(StatusCode::Ok);
26+
/// res.insert_header("hello", "foo0").unwrap();
27+
/// assert_eq!(res["hello"], "foo0");
28+
/// ```
29+
#[derive(Debug, Clone)]
30+
pub struct Headers {
31+
pub(crate) headers: HashMap<HeaderName, HeaderValues>,
32+
}
33+
34+
impl Headers {
35+
/// Create a new instance.
36+
pub(crate) fn new() -> Self {
37+
Self {
38+
headers: HashMap::new(),
39+
}
40+
}
41+
42+
/// Insert a header into the headers.
43+
///
44+
/// Not that this will replace all header values for a given header name.
45+
/// If you wish to add header values for a header name that already exists
46+
/// use `Headers::append`
47+
pub fn insert(
48+
&mut self,
49+
name: impl TryInto<HeaderName>,
50+
values: impl ToHeaderValues,
51+
) -> crate::Result<Option<HeaderValues>> {
52+
let name = name
53+
.try_into()
54+
.map_err(|_| crate::format_err!("Could not convert into header name"))?;
55+
let values: HeaderValues = values.to_header_values()?.collect();
56+
Ok(self.headers.insert(name, values))
57+
}
58+
59+
/// Append a header to the headers.
60+
///
61+
/// Unlike `insert` this function will not override the contents of a header, but insert a
62+
/// header if there aren't any. Or else append to the existing list of headers.
63+
pub fn append(
64+
&mut self,
65+
name: impl TryInto<HeaderName>,
66+
values: impl ToHeaderValues,
67+
) -> crate::Result<()> {
68+
let name = name
69+
.try_into()
70+
.map_err(|_| crate::format_err!("Could not convert into header name"))?;
71+
match self.get_mut(&name) {
72+
Some(headers) => {
73+
let mut values: HeaderValues = values.to_header_values()?.collect();
74+
headers.append(&mut values);
75+
}
76+
None => {
77+
self.insert(name, values)?;
78+
}
79+
}
80+
Ok(())
81+
}
82+
83+
/// Get a reference to a header.
84+
pub fn get(&self, name: &HeaderName) -> Option<&HeaderValues> {
85+
self.headers.get(name)
86+
}
87+
88+
/// Get a mutable reference to a header.
89+
pub fn get_mut(&mut self, name: &HeaderName) -> Option<&mut HeaderValues> {
90+
self.headers.get_mut(name)
91+
}
92+
93+
/// Remove a header.
94+
pub fn remove(&mut self, name: &HeaderName) -> Option<HeaderValues> {
95+
self.headers.remove(name)
96+
}
97+
98+
/// An iterator visiting all header pairs in arbitrary order.
99+
pub fn iter(&self) -> Iter<'_> {
100+
Iter {
101+
inner: self.headers.iter(),
102+
}
103+
}
104+
105+
/// An iterator visiting all header pairs in arbitrary order, with mutable references to the
106+
/// values.
107+
pub fn iter_mut(&mut self) -> IterMut<'_> {
108+
IterMut {
109+
inner: self.headers.iter_mut(),
110+
}
111+
}
112+
113+
/// An iterator visiting all header names in arbitrary order.
114+
pub fn names(&self) -> Names<'_> {
115+
Names {
116+
inner: self.headers.keys(),
117+
}
118+
}
119+
120+
/// An iterator visiting all header values in arbitrary order.
121+
pub fn values(&self) -> Values<'_> {
122+
Values::new(self.headers.values())
123+
}
124+
}
125+
126+
impl Index<&HeaderName> for Headers {
127+
type Output = HeaderValues;
128+
129+
/// Returns a reference to the value corresponding to the supplied name.
130+
///
131+
/// # Panics
132+
///
133+
/// Panics if the name is not present in `Headers`.
134+
#[inline]
135+
fn index(&self, name: &HeaderName) -> &HeaderValues {
136+
self.get(name).expect("no entry found for name")
137+
}
138+
}
139+
140+
impl Index<&str> for Headers {
141+
type Output = HeaderValues;
142+
143+
/// Returns a reference to the value corresponding to the supplied name.
144+
///
145+
/// # Panics
146+
///
147+
/// Panics if the name is not present in `Headers`.
148+
#[inline]
149+
fn index(&self, name: &str) -> &HeaderValues {
150+
let name = HeaderName::from_str(name).expect("string slice needs to be valid ASCII");
151+
self.get(&name).expect("no entry found for name")
152+
}
153+
}
154+
155+
impl IntoIterator for Headers {
156+
type Item = (HeaderName, HeaderValues);
157+
type IntoIter = IntoIter;
158+
159+
/// Returns a iterator of references over the remaining items.
160+
#[inline]
161+
fn into_iter(self) -> Self::IntoIter {
162+
IntoIter {
163+
inner: self.headers.into_iter(),
164+
}
165+
}
166+
}
167+
168+
impl<'a> IntoIterator for &'a Headers {
169+
type Item = (&'a HeaderName, &'a HeaderValues);
170+
type IntoIter = Iter<'a>;
171+
172+
#[inline]
173+
fn into_iter(self) -> Self::IntoIter {
174+
self.iter()
175+
}
176+
}
177+
178+
impl<'a> IntoIterator for &'a mut Headers {
179+
type Item = (&'a HeaderName, &'a mut HeaderValues);
180+
type IntoIter = IterMut<'a>;
181+
182+
#[inline]
183+
fn into_iter(self) -> Self::IntoIter {
184+
self.iter_mut()
185+
}
186+
}
187+
188+
#[cfg(test)]
189+
mod tests {
190+
use super::*;
191+
use std::str::FromStr;
192+
193+
const STATIC_HEADER: HeaderName = HeaderName::from_lowercase_str("hello");
194+
195+
#[test]
196+
fn test_header_name_static_non_static() -> crate::Result<()> {
197+
let static_header = HeaderName::from_lowercase_str("hello");
198+
let non_static_header = HeaderName::from_str("hello")?;
199+
200+
let mut headers = Headers::new();
201+
headers.append(STATIC_HEADER, "foo0")?;
202+
headers.append(static_header.clone(), "foo1")?;
203+
headers.append(non_static_header.clone(), "foo2")?;
204+
205+
assert_eq!(
206+
&headers.get(&STATIC_HEADER).unwrap()[..],
207+
&["foo0", "foo1", "foo2",][..]
208+
);
209+
210+
assert_eq!(
211+
&headers.get(&static_header).unwrap()[..],
212+
&["foo0", "foo1", "foo2",][..]
213+
);
214+
215+
assert_eq!(
216+
&headers.get(&non_static_header).unwrap()[..],
217+
&["foo0", "foo1", "foo2",][..]
218+
);
219+
220+
Ok(())
221+
}
222+
223+
#[test]
224+
fn index_into_headers() {
225+
let mut headers = Headers::new();
226+
headers.insert("hello", "foo0").unwrap();
227+
assert_eq!(headers["hello"], "foo0");
228+
}
229+
}

0 commit comments

Comments
 (0)