http_types/server/
allow.rs

1//! List the set of methods supported by a resource.
2
3use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ALLOW};
4use crate::Method;
5
6use std::collections::{hash_set, HashSet};
7use std::fmt::{self, Debug, Write};
8use std::iter::Iterator;
9use std::option;
10use std::str::FromStr;
11
12/// List the set of methods supported by a resource.
13///
14/// # Specifications
15///
16/// - [RFC 7231, section 7.4.1: Allow](https://tools.ietf.org/html/rfc7231#section-7.4.1)
17///
18/// # Examples
19///
20/// ```
21/// # fn main() -> http_types::Result<()> {
22/// #
23/// use http_types::{Method, Response};
24/// use http_types::server::Allow;
25///
26/// let mut allow = Allow::new();
27/// allow.insert(Method::Put);
28/// allow.insert(Method::Post);
29///
30/// let mut res = Response::new(200);
31/// allow.apply(&mut res);
32///
33/// let allow = Allow::from_headers(res)?.unwrap();
34/// assert!(allow.contains(Method::Put));
35/// assert!(allow.contains(Method::Post));
36/// #
37/// # Ok(()) }
38/// ```
39pub struct Allow {
40    entries: HashSet<Method>,
41}
42
43impl Allow {
44    /// Create a new instance of `Allow`.
45    pub fn new() -> Self {
46        Self {
47            entries: HashSet::new(),
48        }
49    }
50
51    /// Create a new instance from headers.
52    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
53        let mut entries = HashSet::new();
54        let headers = match headers.as_ref().get(ALLOW) {
55            Some(headers) => headers,
56            None => return Ok(None),
57        };
58
59        for value in headers {
60            for part in value.as_str().trim().split(',') {
61                let method = Method::from_str(part.trim())?;
62                entries.insert(method);
63            }
64        }
65
66        Ok(Some(Self { entries }))
67    }
68
69    /// Sets the `Allow` header.
70    pub fn apply(&self, mut headers: impl AsMut<Headers>) {
71        headers.as_mut().insert(ALLOW, self.value());
72    }
73
74    /// Get the `HeaderName`.
75    pub fn name(&self) -> HeaderName {
76        ALLOW
77    }
78
79    /// Get the `HeaderValue`.
80    pub fn value(&self) -> HeaderValue {
81        let mut output = String::new();
82        for (n, method) in self.entries.iter().enumerate() {
83            match n {
84                0 => write!(output, "{}", method).unwrap(),
85                _ => write!(output, ", {}", method).unwrap(),
86            };
87        }
88
89        // SAFETY: the internal string is validated to be ASCII.
90        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
91    }
92
93    /// Push a method into the set of methods.
94    pub fn insert(&mut self, method: Method) {
95        self.entries.insert(method);
96    }
97
98    /// An iterator visiting all server entries.
99    pub fn iter(&self) -> Iter<'_> {
100        Iter {
101            inner: self.entries.iter(),
102        }
103    }
104
105    /// Returns `true` if the header contains the `Method`.
106    pub fn contains(&self, method: Method) -> bool {
107        self.entries.contains(&method)
108    }
109}
110
111impl IntoIterator for Allow {
112    type Item = Method;
113    type IntoIter = IntoIter;
114
115    #[inline]
116    fn into_iter(self) -> Self::IntoIter {
117        IntoIter {
118            inner: self.entries.into_iter(),
119        }
120    }
121}
122
123impl<'a> IntoIterator for &'a Allow {
124    type Item = &'a Method;
125    type IntoIter = Iter<'a>;
126
127    #[inline]
128    fn into_iter(self) -> Self::IntoIter {
129        self.iter()
130    }
131}
132
133/// A borrowing iterator over entries in `Allow`.
134#[derive(Debug)]
135pub struct IntoIter {
136    inner: hash_set::IntoIter<Method>,
137}
138
139impl Iterator for IntoIter {
140    type Item = Method;
141
142    fn next(&mut self) -> Option<Self::Item> {
143        self.inner.next()
144    }
145
146    #[inline]
147    fn size_hint(&self) -> (usize, Option<usize>) {
148        self.inner.size_hint()
149    }
150}
151
152/// A lending iterator over entries in `Allow`.
153#[derive(Debug)]
154pub struct Iter<'a> {
155    inner: hash_set::Iter<'a, Method>,
156}
157
158impl<'a> Iterator for Iter<'a> {
159    type Item = &'a Method;
160
161    fn next(&mut self) -> Option<Self::Item> {
162        self.inner.next()
163    }
164
165    #[inline]
166    fn size_hint(&self) -> (usize, Option<usize>) {
167        self.inner.size_hint()
168    }
169}
170
171impl ToHeaderValues for Allow {
172    type Iter = option::IntoIter<HeaderValue>;
173    fn to_header_values(&self) -> crate::Result<Self::Iter> {
174        // A HeaderValue will always convert into itself.
175        Ok(self.value().to_header_values().unwrap())
176    }
177}
178
179impl Debug for Allow {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        let mut list = f.debug_list();
182        for method in &self.entries {
183            list.entry(method);
184        }
185        list.finish()
186    }
187}
188
189#[cfg(test)]
190mod test {
191    use super::*;
192    use crate::headers::Headers;
193
194    #[test]
195    fn smoke() -> crate::Result<()> {
196        let mut allow = Allow::new();
197        allow.insert(Method::Put);
198        allow.insert(Method::Post);
199
200        let mut headers = Headers::new();
201        allow.apply(&mut headers);
202
203        let allow = Allow::from_headers(headers)?.unwrap();
204        assert!(allow.contains(Method::Put));
205        assert!(allow.contains(Method::Post));
206        Ok(())
207    }
208}