http_types/conditional/
if_none_match.rs1use crate::conditional::ETag;
7use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_NONE_MATCH};
8
9use std::fmt::{self, Debug, Write};
10use std::iter::Iterator;
11use std::option;
12use std::slice;
13
14pub struct IfNoneMatch {
46 entries: Vec<ETag>,
47 wildcard: bool,
48}
49
50impl IfNoneMatch {
51 pub fn new() -> Self {
53 Self {
54 entries: vec![],
55 wildcard: false,
56 }
57 }
58
59 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
61 let mut entries = vec![];
62 let headers = match headers.as_ref().get(IF_NONE_MATCH) {
63 Some(headers) => headers,
64 None => return Ok(None),
65 };
66
67 let mut wildcard = false;
68 for value in headers {
69 for part in value.as_str().trim().split(',') {
70 let part = part.trim();
71 if part == "*" {
72 wildcard = true;
73 continue;
74 }
75 entries.push(ETag::from_str(part)?);
76 }
77 }
78
79 Ok(Some(Self { entries, wildcard }))
80 }
81
82 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
84 headers.as_mut().insert(IF_NONE_MATCH, self.value());
85 }
86
87 pub fn name(&self) -> HeaderName {
89 IF_NONE_MATCH
90 }
91
92 pub fn value(&self) -> HeaderValue {
94 let mut output = String::new();
95 for (n, etag) in self.entries.iter().enumerate() {
96 match n {
97 0 => write!(output, "{}", etag.to_string()).unwrap(),
98 _ => write!(output, ", {}", etag.to_string()).unwrap(),
99 };
100 }
101
102 if self.wildcard {
103 match output.len() {
104 0 => write!(output, "*").unwrap(),
105 _ => write!(output, ", *").unwrap(),
106 };
107 }
108
109 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
111 }
112
113 pub fn push(&mut self, directive: impl Into<ETag>) {
115 self.entries.push(directive.into());
116 }
117
118 pub fn wildcard(&self) -> bool {
120 self.wildcard
121 }
122
123 pub fn set_wildcard(&mut self, wildcard: bool) {
125 self.wildcard = wildcard
126 }
127
128 pub fn iter(&self) -> Iter<'_> {
130 Iter {
131 inner: self.entries.iter(),
132 }
133 }
134
135 pub fn iter_mut(&mut self) -> IterMut<'_> {
137 IterMut {
138 inner: self.entries.iter_mut(),
139 }
140 }
141}
142
143impl IntoIterator for IfNoneMatch {
144 type Item = ETag;
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 IfNoneMatch {
156 type Item = &'a ETag;
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 IfNoneMatch {
166 type Item = &'a mut ETag;
167 type IntoIter = IterMut<'a>;
168
169 #[inline]
170 fn into_iter(self) -> Self::IntoIter {
171 self.iter_mut()
172 }
173}
174
175#[derive(Debug)]
177pub struct IntoIter {
178 inner: std::vec::IntoIter<ETag>,
179}
180
181impl Iterator for IntoIter {
182 type Item = ETag;
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#[derive(Debug)]
196pub struct Iter<'a> {
197 inner: slice::Iter<'a, ETag>,
198}
199
200impl<'a> Iterator for Iter<'a> {
201 type Item = &'a ETag;
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#[derive(Debug)]
215pub struct IterMut<'a> {
216 inner: slice::IterMut<'a, ETag>,
217}
218
219impl<'a> Iterator for IterMut<'a> {
220 type Item = &'a mut ETag;
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 IfNoneMatch {
233 type Iter = option::IntoIter<HeaderValue>;
234 fn to_header_values(&self) -> crate::Result<Self::Iter> {
235 Ok(self.value().to_header_values().unwrap())
237 }
238}
239
240impl Debug for IfNoneMatch {
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::{ETag, IfNoneMatch};
253 use crate::Response;
254
255 #[test]
256 fn smoke() -> crate::Result<()> {
257 let mut entries = IfNoneMatch::new();
258 entries.push(ETag::new("0xcafebeef".to_string()));
259 entries.push(ETag::new("0xbeefcafe".to_string()));
260
261 let mut res = Response::new(200);
262 entries.apply(&mut res);
263
264 let entries = IfNoneMatch::from_headers(res)?.unwrap();
265 let mut entries = entries.iter();
266 assert_eq!(
267 entries.next().unwrap(),
268 &ETag::new("0xcafebeef".to_string())
269 );
270 assert_eq!(
271 entries.next().unwrap(),
272 &ETag::new("0xbeefcafe".to_string())
273 );
274 Ok(())
275 }
276
277 #[test]
278 fn wildcard() -> crate::Result<()> {
279 let mut entries = IfNoneMatch::new();
280 entries.push(ETag::new("0xcafebeef".to_string()));
281 entries.set_wildcard(true);
282
283 let mut res = Response::new(200);
284 entries.apply(&mut res);
285
286 let entries = IfNoneMatch::from_headers(res)?.unwrap();
287 assert!(entries.wildcard());
288 let mut entries = entries.iter();
289 assert_eq!(
290 entries.next().unwrap(),
291 &ETag::new("0xcafebeef".to_string())
292 );
293 Ok(())
294 }
295}