1 use super::{Error, Header, HeaderValue};
2 use http;
3 
4 /// An extension trait adding "typed" methods to `http::HeaderMap`.
5 pub trait HeaderMapExt: self::sealed::Sealed {
6     /// Inserts the typed `Header` into this `HeaderMap`.
typed_insert<H>(&mut self, header: H) where H: Header7     fn typed_insert<H>(&mut self, header: H)
8     where
9         H: Header;
10 
11     /// Tries to find the header by name, and then decode it into `H`.
typed_get<H>(&self) -> Option<H> where H: Header12     fn typed_get<H>(&self) -> Option<H>
13     where
14         H: Header;
15 
16     /// Tries to find the header by name, and then decode it into `H`.
typed_try_get<H>(&self) -> Result<Option<H>, Error> where H: Header17     fn typed_try_get<H>(&self) -> Result<Option<H>, Error>
18     where
19         H: Header;
20 }
21 
22 impl HeaderMapExt for http::HeaderMap {
typed_insert<H>(&mut self, header: H) where H: Header,23     fn typed_insert<H>(&mut self, header: H)
24     where
25         H: Header,
26     {
27         let entry = self.entry(H::name());
28         let mut values = ToValues {
29             state: State::First(entry),
30         };
31         header.encode(&mut values);
32     }
33 
typed_get<H>(&self) -> Option<H> where H: Header,34     fn typed_get<H>(&self) -> Option<H>
35     where
36         H: Header,
37     {
38         HeaderMapExt::typed_try_get(self).unwrap_or(None)
39     }
40 
typed_try_get<H>(&self) -> Result<Option<H>, Error> where H: Header,41     fn typed_try_get<H>(&self) -> Result<Option<H>, Error>
42     where
43         H: Header,
44     {
45         let mut values = self.get_all(H::name()).iter();
46         if values.size_hint() == (0, Some(0)) {
47             Ok(None)
48         } else {
49             H::decode(&mut values).map(Some)
50         }
51     }
52 }
53 
54 struct ToValues<'a> {
55     state: State<'a>,
56 }
57 
58 #[derive(Debug)]
59 enum State<'a> {
60     First(http::header::Entry<'a, HeaderValue>),
61     Latter(http::header::OccupiedEntry<'a, HeaderValue>),
62     Tmp,
63 }
64 
65 impl<'a> Extend<HeaderValue> for ToValues<'a> {
extend<T: IntoIterator<Item = HeaderValue>>(&mut self, iter: T)66     fn extend<T: IntoIterator<Item = HeaderValue>>(&mut self, iter: T) {
67         for value in iter {
68             let entry = match ::std::mem::replace(&mut self.state, State::Tmp) {
69                 State::First(http::header::Entry::Occupied(mut e)) => {
70                     e.insert(value);
71                     e
72                 }
73                 State::First(http::header::Entry::Vacant(e)) => e.insert_entry(value),
74                 State::Latter(mut e) => {
75                     e.append(value);
76                     e
77                 }
78                 State::Tmp => unreachable!("ToValues State::Tmp"),
79             };
80             self.state = State::Latter(entry);
81         }
82     }
83 }
84 
85 mod sealed {
86     pub trait Sealed {}
87     impl Sealed for ::http::HeaderMap {}
88 }
89