http_types_rs/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_rs::Result<()> {
22/// #
23/// use http_types_rs::{Method, Response};
24/// use http_types_rs::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 { entries: HashSet::new() }
47    }
48
49    /// Create a new instance from headers.
50    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
51        let mut entries = HashSet::new();
52        let headers = match headers.as_ref().get(ALLOW) {
53            Some(headers) => headers,
54            None => return Ok(None),
55        };
56
57        for value in headers {
58            for part in value.as_str().trim().split(',') {
59                let method = Method::from_str(part.trim())?;
60                entries.insert(method);
61            }
62        }
63
64        Ok(Some(Self { entries }))
65    }
66
67    /// Push a method into the set of methods.
68    pub fn insert(&mut self, method: Method) {
69        self.entries.insert(method);
70    }
71
72    /// An iterator visiting all server entries.
73    pub fn iter(&self) -> Iter<'_> {
74        Iter { inner: self.entries.iter() }
75    }
76
77    /// Returns `true` if the header contains the `Method`.
78    pub fn contains(&self, method: Method) -> bool {
79        self.entries.contains(&method)
80    }
81}
82
83impl Header for Allow {
84    fn header_name(&self) -> HeaderName {
85        ALLOW
86    }
87    fn header_value(&self) -> HeaderValue {
88        let mut output = String::new();
89        for (n, method) in self.entries.iter().enumerate() {
90            match n {
91                0 => write!(output, "{}", method).unwrap(),
92                _ => write!(output, ", {}", method).unwrap(),
93            };
94        }
95
96        // SAFETY: the internal string is validated to be ASCII.
97        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
98    }
99}
100
101impl IntoIterator for Allow {
102    type Item = Method;
103    type IntoIter = IntoIter;
104
105    #[inline]
106    fn into_iter(self) -> Self::IntoIter {
107        IntoIter {
108            inner: self.entries.into_iter(),
109        }
110    }
111}
112
113impl<'a> IntoIterator for &'a Allow {
114    type Item = &'a Method;
115    type IntoIter = Iter<'a>;
116
117    #[inline]
118    fn into_iter(self) -> Self::IntoIter {
119        self.iter()
120    }
121}
122
123/// A borrowing iterator over entries in `Allow`.
124#[derive(Debug)]
125pub struct IntoIter {
126    inner: hash_set::IntoIter<Method>,
127}
128
129impl Iterator for IntoIter {
130    type Item = Method;
131
132    fn next(&mut self) -> Option<Self::Item> {
133        self.inner.next()
134    }
135
136    #[inline]
137    fn size_hint(&self) -> (usize, Option<usize>) {
138        self.inner.size_hint()
139    }
140}
141
142/// A lending iterator over entries in `Allow`.
143#[derive(Debug)]
144pub struct Iter<'a> {
145    inner: hash_set::Iter<'a, Method>,
146}
147
148impl<'a> Iterator for Iter<'a> {
149    type Item = &'a Method;
150
151    fn next(&mut self) -> Option<Self::Item> {
152        self.inner.next()
153    }
154
155    #[inline]
156    fn size_hint(&self) -> (usize, Option<usize>) {
157        self.inner.size_hint()
158    }
159}
160
161impl Debug for Allow {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        let mut list = f.debug_list();
164        for method in &self.entries {
165            list.entry(method);
166        }
167        list.finish()
168    }
169}
170
171#[cfg(test)]
172mod test {
173    use super::*;
174    use crate::headers::Headers;
175
176    #[test]
177    fn smoke() -> crate::Result<()> {
178        let mut allow = Allow::new();
179        allow.insert(Method::Put);
180        allow.insert(Method::Post);
181
182        let mut headers = Headers::new();
183        allow.apply_header(&mut headers);
184
185        let allow = Allow::from_headers(headers)?.unwrap();
186        assert!(allow.contains(Method::Put));
187        assert!(allow.contains(Method::Post));
188        Ok(())
189    }
190}