http_types/cache/clear_site_data/
mod.rs1use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CLEAR_SITE_DATA};
5
6use std::fmt::{self, Debug, Write};
7use std::iter::Iterator;
8use std::option;
9use std::slice;
10use std::str::FromStr;
11
12mod directive;
13
14pub use directive::ClearDirective;
15
16pub struct ClearSiteData {
48 entries: Vec<ClearDirective>,
49 wildcard: bool,
50}
51
52impl ClearSiteData {
53 pub fn new() -> Self {
55 Self {
56 entries: vec![],
57 wildcard: false,
58 }
59 }
60
61 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
63 let mut entries = vec![];
64 let header_values = match headers.as_ref().get(CLEAR_SITE_DATA) {
65 Some(headers) => headers,
66 None => return Ok(None),
67 };
68
69 let mut wildcard = false;
70 for value in header_values {
71 for part in value.as_str().trim().split(',') {
72 let part = part.trim();
73 if part == r#""*""# {
74 wildcard = true;
75 continue;
76 }
77 entries.push(ClearDirective::from_str(part)?);
78 }
79 }
80
81 Ok(Some(Self { entries, wildcard }))
82 }
83
84 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
86 headers.as_mut().insert(CLEAR_SITE_DATA, self.value());
87 }
88
89 pub fn name(&self) -> HeaderName {
91 CLEAR_SITE_DATA
92 }
93
94 pub fn value(&self) -> HeaderValue {
96 let mut output = String::new();
97 for (n, etag) in self.entries.iter().enumerate() {
98 match n {
99 0 => write!(output, "{}", etag.to_string()).unwrap(),
100 _ => write!(output, ", {}", etag.to_string()).unwrap(),
101 };
102 }
103
104 if self.wildcard {
105 match output.len() {
106 0 => write!(output, r#""*""#).unwrap(),
107 _ => write!(output, r#", "*""#).unwrap(),
108 };
109 }
110
111 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
113 }
114
115 pub fn push(&mut self, directive: impl Into<ClearDirective>) {
117 self.entries.push(directive.into());
118 }
119
120 pub fn wildcard(&self) -> bool {
122 self.wildcard
123 }
124
125 pub fn set_wildcard(&mut self, wildcard: bool) {
127 self.wildcard = wildcard
128 }
129
130 pub fn iter(&self) -> Iter<'_> {
132 Iter {
133 inner: self.entries.iter(),
134 }
135 }
136
137 pub fn iter_mut(&mut self) -> IterMut<'_> {
139 IterMut {
140 inner: self.entries.iter_mut(),
141 }
142 }
143}
144
145impl IntoIterator for ClearSiteData {
146 type Item = ClearDirective;
147 type IntoIter = IntoIter;
148
149 #[inline]
150 fn into_iter(self) -> Self::IntoIter {
151 IntoIter {
152 inner: self.entries.into_iter(),
153 }
154 }
155}
156
157impl<'a> IntoIterator for &'a ClearSiteData {
158 type Item = &'a ClearDirective;
159 type IntoIter = Iter<'a>;
160
161 #[inline]
162 fn into_iter(self) -> Self::IntoIter {
163 self.iter()
164 }
165}
166
167impl<'a> IntoIterator for &'a mut ClearSiteData {
168 type Item = &'a mut ClearDirective;
169 type IntoIter = IterMut<'a>;
170
171 #[inline]
172 fn into_iter(self) -> Self::IntoIter {
173 self.iter_mut()
174 }
175}
176
177#[derive(Debug)]
179pub struct IntoIter {
180 inner: std::vec::IntoIter<ClearDirective>,
181}
182
183impl Iterator for IntoIter {
184 type Item = ClearDirective;
185
186 fn next(&mut self) -> Option<Self::Item> {
187 self.inner.next()
188 }
189
190 #[inline]
191 fn size_hint(&self) -> (usize, Option<usize>) {
192 self.inner.size_hint()
193 }
194}
195
196#[derive(Debug)]
198pub struct Iter<'a> {
199 inner: slice::Iter<'a, ClearDirective>,
200}
201
202impl<'a> Iterator for Iter<'a> {
203 type Item = &'a ClearDirective;
204
205 fn next(&mut self) -> Option<Self::Item> {
206 self.inner.next()
207 }
208
209 #[inline]
210 fn size_hint(&self) -> (usize, Option<usize>) {
211 self.inner.size_hint()
212 }
213}
214
215#[derive(Debug)]
217pub struct IterMut<'a> {
218 inner: slice::IterMut<'a, ClearDirective>,
219}
220
221impl<'a> Iterator for IterMut<'a> {
222 type Item = &'a mut ClearDirective;
223
224 fn next(&mut self) -> Option<Self::Item> {
225 self.inner.next()
226 }
227
228 #[inline]
229 fn size_hint(&self) -> (usize, Option<usize>) {
230 self.inner.size_hint()
231 }
232}
233
234impl ToHeaderValues for ClearSiteData {
235 type Iter = option::IntoIter<HeaderValue>;
236 fn to_header_values(&self) -> crate::Result<Self::Iter> {
237 Ok(self.value().to_header_values().unwrap())
239 }
240}
241
242impl Debug for ClearSiteData {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 let mut list = f.debug_list();
245 for directive in &self.entries {
246 list.entry(directive);
247 }
248 list.finish()
249 }
250}
251
252#[cfg(test)]
253mod test {
254 use crate::cache::{ClearDirective, ClearSiteData};
255 use crate::Response;
256
257 #[test]
258 fn smoke() -> crate::Result<()> {
259 let mut entries = ClearSiteData::new();
260 entries.push(ClearDirective::Cache);
261 entries.push(ClearDirective::Cookies);
262
263 let mut res = Response::new(200);
264 entries.apply(&mut res);
265
266 let entries = ClearSiteData::from_headers(res)?.unwrap();
267 let mut entries = entries.iter();
268 assert_eq!(entries.next().unwrap(), &ClearDirective::Cache);
269 assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies);
270 Ok(())
271 }
272
273 #[test]
274 fn wildcard() -> crate::Result<()> {
275 let mut entries = ClearSiteData::new();
276 entries.push(ClearDirective::Cache);
277 entries.set_wildcard(true);
278
279 let mut res = Response::new(200);
280 entries.apply(&mut res);
281
282 let entries = ClearSiteData::from_headers(res)?.unwrap();
283 assert!(entries.wildcard());
284 let mut entries = entries.iter();
285 assert_eq!(entries.next().unwrap(), &ClearDirective::Cache);
286 Ok(())
287 }
288
289 #[test]
290 fn parse_quotes_correctly() -> crate::Result<()> {
291 let mut res = Response::new(200);
292 res.insert_header("clear-site-data", r#""cookies""#);
293
294 let entries = ClearSiteData::from_headers(res)?.unwrap();
295 assert!(!entries.wildcard());
296 let mut entries = entries.iter();
297 assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies);
298
299 let mut res = Response::new(200);
300 res.insert_header("clear-site-data", r#""*""#);
301
302 let entries = ClearSiteData::from_headers(res)?.unwrap();
303 assert!(entries.wildcard());
304 let mut entries = entries.iter();
305 assert_eq!(entries.next(), None);
306 Ok(())
307 }
308}