http_types_2/server/
allow.rs

1//! List the set of methods supported by a resource.
2
3use crate::headers::{Header, HeaderName, HeaderValue, Headers, ALLOW};
4use crate::Method;
5
6use std::collections::{hash_set, HashSet};
7use std::fmt::{self, Debug, Write};
8use std::iter::Iterator;
9
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/// res.insert_header(&allow, &allow);
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    /// Push a method into the set of methods.
70    pub fn insert(&mut self, method: Method) {
71        self.entries.insert(method);
72    }
73
74    /// An iterator visiting all server entries.
75    pub fn iter(&self) -> Iter<'_> {
76        Iter {
77            inner: self.entries.iter(),
78        }
79    }
80
81    /// Returns `true` if the header contains the `Method`.
82    pub fn contains(&self, method: Method) -> bool {
83        self.entries.contains(&method)
84    }
85}
86
87impl Header for Allow {
88    fn header_name(&self) -> HeaderName {
89        ALLOW
90    }
91    fn header_value(&self) -> HeaderValue {
92        let mut output = String::new();
93        for (n, method) in self.entries.iter().enumerate() {
94            match n {
95                0 => write!(output, "{method}").unwrap(),
96                _ => write!(output, ", {method}").unwrap(),
97            };
98        }
99
100        // SAFETY: the internal string is validated to be ASCII.
101        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
102    }
103}
104
105impl IntoIterator for Allow {
106    type Item = Method;
107    type IntoIter = IntoIter;
108
109    #[inline]
110    fn into_iter(self) -> Self::IntoIter {
111        IntoIter {
112            inner: self.entries.into_iter(),
113        }
114    }
115}
116
117impl<'a> IntoIterator for &'a Allow {
118    type Item = &'a Method;
119    type IntoIter = Iter<'a>;
120
121    #[inline]
122    fn into_iter(self) -> Self::IntoIter {
123        self.iter()
124    }
125}
126
127/// A borrowing iterator over entries in `Allow`.
128#[derive(Debug)]
129pub struct IntoIter {
130    inner: hash_set::IntoIter<Method>,
131}
132
133impl Iterator for IntoIter {
134    type Item = Method;
135
136    fn next(&mut self) -> Option<Self::Item> {
137        self.inner.next()
138    }
139
140    #[inline]
141    fn size_hint(&self) -> (usize, Option<usize>) {
142        self.inner.size_hint()
143    }
144}
145
146/// A lending iterator over entries in `Allow`.
147#[derive(Debug)]
148pub struct Iter<'a> {
149    inner: hash_set::Iter<'a, Method>,
150}
151
152impl<'a> Iterator for Iter<'a> {
153    type Item = &'a Method;
154
155    fn next(&mut self) -> Option<Self::Item> {
156        self.inner.next()
157    }
158
159    #[inline]
160    fn size_hint(&self) -> (usize, Option<usize>) {
161        self.inner.size_hint()
162    }
163}
164
165impl Debug for Allow {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        let mut list = f.debug_list();
168        for method in &self.entries {
169            list.entry(method);
170        }
171        list.finish()
172    }
173}
174
175#[cfg(test)]
176mod test {
177    use super::*;
178    use crate::headers::Headers;
179
180    #[test]
181    fn smoke() -> crate::Result<()> {
182        let mut allow = Allow::new();
183        allow.insert(Method::Put);
184        allow.insert(Method::Post);
185
186        let mut headers = Headers::new();
187        allow.apply_header(&mut headers);
188
189        let allow = Allow::from_headers(headers)?.unwrap();
190        assert!(allow.contains(Method::Put));
191        assert!(allow.contains(Method::Post));
192        Ok(())
193    }
194}