http_types/conditional/
vary.rs

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