http_types_2/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::Result<()> {
21/// #
22/// use http_types::Response;
23/// use http_types::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 {
96            inner: self.entries.iter(),
97        }
98    }
99
100    /// An iterator visiting all server entries.
101    pub fn iter_mut(&mut self) -> IterMut<'_> {
102        IterMut {
103            inner: self.entries.iter_mut(),
104        }
105    }
106}
107
108impl Header for Vary {
109    fn header_name(&self) -> HeaderName {
110        VARY
111    }
112
113    fn header_value(&self) -> HeaderValue {
114        let mut output = String::new();
115        for (n, name) in self.entries.iter().enumerate() {
116            let directive: HeaderValue = name
117                .as_str()
118                .parse()
119                .expect("Could not convert a HeaderName into a HeaderValue");
120            match n {
121                0 => write!(output, "{directive}").unwrap(),
122                _ => write!(output, ", {directive}").unwrap(),
123            };
124        }
125
126        if self.wildcard {
127            match output.len() {
128                0 => write!(output, "*").unwrap(),
129                _ => write!(output, ", *").unwrap(),
130            };
131        }
132
133        // SAFETY: the internal string is validated to be ASCII.
134        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
135    }
136}
137
138impl IntoIterator for Vary {
139    type Item = HeaderName;
140    type IntoIter = IntoIter;
141
142    #[inline]
143    fn into_iter(self) -> Self::IntoIter {
144        IntoIter {
145            inner: self.entries.into_iter(),
146        }
147    }
148}
149
150impl<'a> IntoIterator for &'a Vary {
151    type Item = &'a HeaderName;
152    type IntoIter = Iter<'a>;
153
154    #[inline]
155    fn into_iter(self) -> Self::IntoIter {
156        self.iter()
157    }
158}
159
160impl<'a> IntoIterator for &'a mut Vary {
161    type Item = &'a mut HeaderName;
162    type IntoIter = IterMut<'a>;
163
164    #[inline]
165    fn into_iter(self) -> Self::IntoIter {
166        self.iter_mut()
167    }
168}
169
170/// A borrowing iterator over entries in `Vary`.
171#[derive(Debug)]
172pub struct IntoIter {
173    inner: std::vec::IntoIter<HeaderName>,
174}
175
176impl Iterator for IntoIter {
177    type Item = HeaderName;
178
179    fn next(&mut self) -> Option<Self::Item> {
180        self.inner.next()
181    }
182
183    #[inline]
184    fn size_hint(&self) -> (usize, Option<usize>) {
185        self.inner.size_hint()
186    }
187}
188
189/// A lending iterator over entries in `Vary`.
190#[derive(Debug)]
191pub struct Iter<'a> {
192    inner: slice::Iter<'a, HeaderName>,
193}
194
195impl<'a> Iterator for Iter<'a> {
196    type Item = &'a HeaderName;
197
198    fn next(&mut self) -> Option<Self::Item> {
199        self.inner.next()
200    }
201
202    #[inline]
203    fn size_hint(&self) -> (usize, Option<usize>) {
204        self.inner.size_hint()
205    }
206}
207
208/// A mutable iterator over entries in `Vary`.
209#[derive(Debug)]
210pub struct IterMut<'a> {
211    inner: slice::IterMut<'a, HeaderName>,
212}
213
214impl<'a> Iterator for IterMut<'a> {
215    type Item = &'a mut HeaderName;
216
217    fn next(&mut self) -> Option<Self::Item> {
218        self.inner.next()
219    }
220
221    #[inline]
222    fn size_hint(&self) -> (usize, Option<usize>) {
223        self.inner.size_hint()
224    }
225}
226
227impl Debug for Vary {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        let mut list = f.debug_list();
230        for directive in &self.entries {
231            list.entry(directive);
232        }
233        list.finish()
234    }
235}
236
237#[cfg(test)]
238mod test {
239    use super::*;
240    use crate::conditional::Vary;
241    use crate::Response;
242
243    #[test]
244    fn smoke() -> crate::Result<()> {
245        let mut entries = Vary::new();
246        entries.push("User-Agent")?;
247        entries.push("Accept-Encoding")?;
248
249        let mut res = Response::new(200);
250        entries.apply_header(&mut res);
251
252        let entries = Vary::from_headers(res)?.unwrap();
253        let mut entries = entries.iter();
254        assert_eq!(entries.next().unwrap(), "User-Agent");
255        assert_eq!(entries.next().unwrap(), "Accept-Encoding");
256        Ok(())
257    }
258
259    #[test]
260    fn wildcard() -> crate::Result<()> {
261        let mut entries = Vary::new();
262        entries.push("User-Agent")?;
263        entries.set_wildcard(true);
264
265        let mut res = Response::new(200);
266        entries.apply_header(&mut res);
267
268        let entries = Vary::from_headers(res)?.unwrap();
269        assert!(entries.wildcard());
270        let mut entries = entries.iter();
271        assert_eq!(entries.next().unwrap(), "User-Agent");
272        Ok(())
273    }
274}