Skip to main content

http_types_rs/conditional/
vary.rs

1//! Apply the HTTP method if the ETag matches.
2
3use crate::headers::{Header, HeaderName, HeaderValue, Headers, VARY};
4
5use std::fmt::{self, Debug, Write};
6use std::iter::Iterator;
7
8use std::slice;
9use std::str::FromStr;
10
11/// Apply the HTTP method if the ETag matches.
12///
13/// # Specifications
14///
15/// - [RFC 7231, section 7.1.4: Vary](https://tools.ietf.org/html/rfc7231#section-7.1.4)
16///
17/// # Examples
18///
19/// ```
20/// # fn main() -> http_types_rs::Result<()> {
21/// #
22/// use http_types_rs::Response;
23/// use http_types_rs::conditional::Vary;
24///
25/// let mut entries = Vary::new();
26/// entries.push("User-Agent")?;
27/// entries.push("Accept-Encoding")?;
28///
29/// let mut res = Response::new(200);
30/// res.insert_header(&entries, &entries);
31///
32/// let entries = Vary::from_headers(res)?.unwrap();
33/// let mut entries = entries.iter();
34/// assert_eq!(entries.next().unwrap(), "User-Agent");
35/// assert_eq!(entries.next().unwrap(), "Accept-Encoding");
36/// #
37/// # Ok(()) }
38/// ```
39pub struct Vary {
40    entries: Vec<HeaderName>,
41    wildcard: bool,
42}
43
44impl Vary {
45    /// Create a new instance of `Vary`.
46    pub fn new() -> Self {
47        Self {
48            entries: vec![],
49            wildcard: false,
50        }
51    }
52
53    /// Create a new instance from headers.
54    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
55        let mut entries = vec![];
56        let headers = match headers.as_ref().get(VARY) {
57            Some(headers) => headers,
58            None => return Ok(None),
59        };
60
61        let mut wildcard = false;
62        for value in headers {
63            for part in value.as_str().trim().split(',') {
64                let part = part.trim();
65                if part == "*" {
66                    wildcard = true;
67                    continue;
68                }
69                let entry = HeaderName::from_str(part.trim())?;
70                entries.push(entry);
71            }
72        }
73
74        Ok(Some(Self { entries, wildcard }))
75    }
76
77    /// Returns `true` if a wildcard directive was set.
78    pub fn wildcard(&self) -> bool {
79        self.wildcard
80    }
81
82    /// Set the wildcard directive.
83    pub fn set_wildcard(&mut self, wildcard: bool) {
84        self.wildcard = wildcard
85    }
86
87    /// Push a directive into the list of entries.
88    pub fn push(&mut self, directive: impl Into<HeaderName>) -> crate::Result<()> {
89        self.entries.push(directive.into());
90        Ok(())
91    }
92
93    /// An iterator visiting all server entries.
94    pub fn iter(&self) -> Iter<'_> {
95        Iter { inner: self.entries.iter() }
96    }
97
98    /// An iterator visiting all server entries.
99    pub fn iter_mut(&mut self) -> IterMut<'_> {
100        IterMut {
101            inner: self.entries.iter_mut(),
102        }
103    }
104}
105
106impl Header for Vary {
107    fn header_name(&self) -> HeaderName {
108        VARY
109    }
110
111    fn header_value(&self) -> HeaderValue {
112        let mut output = String::new();
113        for (n, name) in self.entries.iter().enumerate() {
114            let directive: HeaderValue = name.as_str().parse().expect("Could not convert a HeaderName into a HeaderValue");
115            match n {
116                0 => write!(output, "{}", directive).unwrap(),
117                _ => write!(output, ", {}", directive).unwrap(),
118            };
119        }
120
121        if self.wildcard {
122            match output.len() {
123                0 => write!(output, "*").unwrap(),
124                _ => write!(output, ", *").unwrap(),
125            };
126        }
127
128        // SAFETY: the internal string is validated to be ASCII.
129        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
130    }
131}
132
133impl IntoIterator for Vary {
134    type Item = HeaderName;
135    type IntoIter = IntoIter;
136
137    #[inline]
138    fn into_iter(self) -> Self::IntoIter {
139        IntoIter {
140            inner: self.entries.into_iter(),
141        }
142    }
143}
144
145impl<'a> IntoIterator for &'a Vary {
146    type Item = &'a HeaderName;
147    type IntoIter = Iter<'a>;
148
149    #[inline]
150    fn into_iter(self) -> Self::IntoIter {
151        self.iter()
152    }
153}
154
155impl<'a> IntoIterator for &'a mut Vary {
156    type Item = &'a mut HeaderName;
157    type IntoIter = IterMut<'a>;
158
159    #[inline]
160    fn into_iter(self) -> Self::IntoIter {
161        self.iter_mut()
162    }
163}
164
165/// A borrowing iterator over entries in `Vary`.
166#[derive(Debug)]
167pub struct IntoIter {
168    inner: std::vec::IntoIter<HeaderName>,
169}
170
171impl Iterator for IntoIter {
172    type Item = HeaderName;
173
174    fn next(&mut self) -> Option<Self::Item> {
175        self.inner.next()
176    }
177
178    #[inline]
179    fn size_hint(&self) -> (usize, Option<usize>) {
180        self.inner.size_hint()
181    }
182}
183
184/// A lending iterator over entries in `Vary`.
185#[derive(Debug)]
186pub struct Iter<'a> {
187    inner: slice::Iter<'a, HeaderName>,
188}
189
190impl<'a> Iterator for Iter<'a> {
191    type Item = &'a HeaderName;
192
193    fn next(&mut self) -> Option<Self::Item> {
194        self.inner.next()
195    }
196
197    #[inline]
198    fn size_hint(&self) -> (usize, Option<usize>) {
199        self.inner.size_hint()
200    }
201}
202
203/// A mutable iterator over entries in `Vary`.
204#[derive(Debug)]
205pub struct IterMut<'a> {
206    inner: slice::IterMut<'a, HeaderName>,
207}
208
209impl<'a> Iterator for IterMut<'a> {
210    type Item = &'a mut HeaderName;
211
212    fn next(&mut self) -> Option<Self::Item> {
213        self.inner.next()
214    }
215
216    #[inline]
217    fn size_hint(&self) -> (usize, Option<usize>) {
218        self.inner.size_hint()
219    }
220}
221
222impl Debug for Vary {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        let mut list = f.debug_list();
225        for directive in &self.entries {
226            list.entry(directive);
227        }
228        list.finish()
229    }
230}
231
232#[cfg(test)]
233mod test {
234    use super::*;
235    use crate::conditional::Vary;
236    use crate::Response;
237
238    #[test]
239    fn smoke() -> crate::Result<()> {
240        let mut entries = Vary::new();
241        entries.push("User-Agent")?;
242        entries.push("Accept-Encoding")?;
243
244        let mut res = Response::new(200);
245        entries.apply_header(&mut res);
246
247        let entries = Vary::from_headers(res)?.unwrap();
248        let mut entries = entries.iter();
249        assert_eq!(entries.next().unwrap(), "User-Agent");
250        assert_eq!(entries.next().unwrap(), "Accept-Encoding");
251        Ok(())
252    }
253
254    #[test]
255    fn wildcard() -> crate::Result<()> {
256        let mut entries = Vary::new();
257        entries.push("User-Agent")?;
258        entries.set_wildcard(true);
259
260        let mut res = Response::new(200);
261        entries.apply_header(&mut res);
262
263        let entries = Vary::from_headers(res)?.unwrap();
264        assert!(entries.wildcard());
265        let mut entries = entries.iter();
266        assert_eq!(entries.next().unwrap(), "User-Agent");
267        Ok(())
268    }
269}