http_types/content/
accept.rs1use crate::content::{ContentType, MediaTypeProposal};
4use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT};
5use crate::utils::sort_by_weight;
6use crate::{Error, Mime, StatusCode};
7
8use std::fmt::{self, Debug, Write};
9use std::option;
10use std::slice;
11
12pub struct Accept {
48 wildcard: bool,
49 entries: Vec<MediaTypeProposal>,
50}
51
52impl Accept {
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 headers = match headers.as_ref().get(ACCEPT) {
65 Some(headers) => headers,
66 None => return Ok(None),
67 };
68
69 let mut wildcard = false;
70
71 for value in headers {
72 for part in value.as_str().trim().split(',') {
73 let part = part.trim();
74
75 if part.is_empty() {
77 continue;
78 } else if part == "*" {
79 wildcard = true;
80 continue;
81 }
82
83 let entry = MediaTypeProposal::from_str(part)?;
86 entries.push(entry);
87 }
88 }
89
90 Ok(Some(Self { entries, wildcard }))
91 }
92
93 pub fn push(&mut self, prop: impl Into<MediaTypeProposal>) {
95 self.entries.push(prop.into());
96 }
97
98 pub fn wildcard(&self) -> bool {
100 self.wildcard
101 }
102
103 pub fn set_wildcard(&mut self, wildcard: bool) {
105 self.wildcard = wildcard
106 }
107
108 pub fn sort(&mut self) {
114 sort_by_weight(&mut self.entries);
115 }
116
117 pub fn negotiate(&mut self, available: &[Mime]) -> crate::Result<ContentType> {
123 self.sort();
125
126 for accept in &self.entries {
128 if available.contains(accept) {
129 return Ok(accept.media_type.clone().into());
130 }
131 }
132
133 if self.wildcard {
135 if let Some(accept) = available.iter().next() {
136 return Ok(accept.clone().into());
137 }
138 }
139
140 let mut err = Error::new_adhoc("No suitable Content-Type found");
141 err.set_status(StatusCode::NotAcceptable);
142 Err(err)
143 }
144
145 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
147 headers.as_mut().insert(ACCEPT, self.value());
148 }
149
150 pub fn name(&self) -> HeaderName {
152 ACCEPT
153 }
154
155 pub fn value(&self) -> HeaderValue {
157 let mut output = String::new();
158 for (n, directive) in self.entries.iter().enumerate() {
159 let directive: HeaderValue = directive.clone().into();
160 match n {
161 0 => write!(output, "{}", directive).unwrap(),
162 _ => write!(output, ", {}", directive).unwrap(),
163 };
164 }
165
166 if self.wildcard {
167 match output.len() {
168 0 => write!(output, "*").unwrap(),
169 _ => write!(output, ", *").unwrap(),
170 }
171 }
172
173 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
175 }
176
177 pub fn iter(&self) -> Iter<'_> {
179 Iter {
180 inner: self.entries.iter(),
181 }
182 }
183
184 pub fn iter_mut(&mut self) -> IterMut<'_> {
186 IterMut {
187 inner: self.entries.iter_mut(),
188 }
189 }
190}
191
192impl IntoIterator for Accept {
193 type Item = MediaTypeProposal;
194 type IntoIter = IntoIter;
195
196 #[inline]
197 fn into_iter(self) -> Self::IntoIter {
198 IntoIter {
199 inner: self.entries.into_iter(),
200 }
201 }
202}
203
204impl<'a> IntoIterator for &'a Accept {
205 type Item = &'a MediaTypeProposal;
206 type IntoIter = Iter<'a>;
207
208 #[inline]
209 fn into_iter(self) -> Self::IntoIter {
210 self.iter()
211 }
212}
213
214impl<'a> IntoIterator for &'a mut Accept {
215 type Item = &'a mut MediaTypeProposal;
216 type IntoIter = IterMut<'a>;
217
218 #[inline]
219 fn into_iter(self) -> Self::IntoIter {
220 self.iter_mut()
221 }
222}
223
224#[derive(Debug)]
226pub struct IntoIter {
227 inner: std::vec::IntoIter<MediaTypeProposal>,
228}
229
230impl Iterator for IntoIter {
231 type Item = MediaTypeProposal;
232
233 fn next(&mut self) -> Option<Self::Item> {
234 self.inner.next()
235 }
236
237 #[inline]
238 fn size_hint(&self) -> (usize, Option<usize>) {
239 self.inner.size_hint()
240 }
241}
242
243#[derive(Debug)]
245pub struct Iter<'a> {
246 inner: slice::Iter<'a, MediaTypeProposal>,
247}
248
249impl<'a> Iterator for Iter<'a> {
250 type Item = &'a MediaTypeProposal;
251
252 fn next(&mut self) -> Option<Self::Item> {
253 self.inner.next()
254 }
255
256 #[inline]
257 fn size_hint(&self) -> (usize, Option<usize>) {
258 self.inner.size_hint()
259 }
260}
261
262#[derive(Debug)]
264pub struct IterMut<'a> {
265 inner: slice::IterMut<'a, MediaTypeProposal>,
266}
267
268impl<'a> Iterator for IterMut<'a> {
269 type Item = &'a mut MediaTypeProposal;
270
271 fn next(&mut self) -> Option<Self::Item> {
272 self.inner.next()
273 }
274
275 #[inline]
276 fn size_hint(&self) -> (usize, Option<usize>) {
277 self.inner.size_hint()
278 }
279}
280
281impl ToHeaderValues for Accept {
282 type Iter = option::IntoIter<HeaderValue>;
283 fn to_header_values(&self) -> crate::Result<Self::Iter> {
284 Ok(self.value().to_header_values().unwrap())
286 }
287}
288
289impl Debug for Accept {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 let mut list = f.debug_list();
292 for directive in &self.entries {
293 list.entry(directive);
294 }
295 list.finish()
296 }
297}
298
299#[cfg(test)]
300mod test {
301 use super::*;
302 use crate::mime;
303 use crate::Response;
304
305 #[test]
306 fn smoke() -> crate::Result<()> {
307 let mut accept = Accept::new();
308 accept.push(mime::HTML);
309
310 let mut headers = Response::new(200);
311 accept.apply(&mut headers);
312
313 let accept = Accept::from_headers(headers)?.unwrap();
314 assert_eq!(accept.iter().next().unwrap(), mime::HTML);
315 Ok(())
316 }
317
318 #[test]
319 fn wildcard() -> crate::Result<()> {
320 let mut accept = Accept::new();
321 accept.set_wildcard(true);
322
323 let mut headers = Response::new(200);
324 accept.apply(&mut headers);
325
326 let accept = Accept::from_headers(headers)?.unwrap();
327 assert!(accept.wildcard());
328 Ok(())
329 }
330
331 #[test]
332 fn wildcard_and_header() -> crate::Result<()> {
333 let mut accept = Accept::new();
334 accept.push(mime::HTML);
335 accept.set_wildcard(true);
336
337 let mut headers = Response::new(200);
338 accept.apply(&mut headers);
339
340 let accept = Accept::from_headers(headers)?.unwrap();
341 assert!(accept.wildcard());
342 assert_eq!(accept.iter().next().unwrap(), mime::HTML);
343 Ok(())
344 }
345
346 #[test]
347 fn iter() -> crate::Result<()> {
348 let mut accept = Accept::new();
349 accept.push(mime::HTML);
350 accept.push(mime::XML);
351
352 let mut headers = Response::new(200);
353 accept.apply(&mut headers);
354
355 let accept = Accept::from_headers(headers)?.unwrap();
356 let mut accept = accept.iter();
357 assert_eq!(accept.next().unwrap(), mime::HTML);
358 assert_eq!(accept.next().unwrap(), mime::XML);
359 Ok(())
360 }
361
362 #[test]
363 fn reorder_based_on_weight() -> crate::Result<()> {
364 let mut accept = Accept::new();
365 accept.push(MediaTypeProposal::new(mime::HTML, Some(0.4))?);
366 accept.push(MediaTypeProposal::new(mime::XML, None)?);
367 accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?);
368
369 let mut headers = Response::new(200);
370 accept.apply(&mut headers);
371
372 let mut accept = Accept::from_headers(headers)?.unwrap();
373 accept.sort();
374 let mut accept = accept.iter();
375 assert_eq!(accept.next().unwrap(), mime::PLAIN);
376 assert_eq!(accept.next().unwrap(), mime::HTML);
377 assert_eq!(accept.next().unwrap(), mime::XML);
378 Ok(())
379 }
380
381 #[test]
382 fn reorder_based_on_weight_and_location() -> crate::Result<()> {
383 let mut accept = Accept::new();
384 accept.push(MediaTypeProposal::new(mime::HTML, None)?);
385 accept.push(MediaTypeProposal::new(mime::XML, None)?);
386 accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?);
387
388 let mut res = Response::new(200);
389 accept.apply(&mut res);
390
391 let mut accept = Accept::from_headers(res)?.unwrap();
392 accept.sort();
393 let mut accept = accept.iter();
394 assert_eq!(accept.next().unwrap(), mime::PLAIN);
395 assert_eq!(accept.next().unwrap(), mime::XML);
396 assert_eq!(accept.next().unwrap(), mime::HTML);
397 Ok(())
398 }
399
400 #[test]
401 fn negotiate() -> crate::Result<()> {
402 let mut accept = Accept::new();
403 accept.push(MediaTypeProposal::new(mime::HTML, Some(0.4))?);
404 accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?);
405 accept.push(MediaTypeProposal::new(mime::XML, None)?);
406
407 assert_eq!(accept.negotiate(&[mime::HTML, mime::XML])?, mime::HTML);
408 Ok(())
409 }
410
411 #[test]
412 fn negotiate_not_acceptable() -> crate::Result<()> {
413 let mut accept = Accept::new();
414 let err = accept.negotiate(&[mime::JSON]).unwrap_err();
415 assert_eq!(err.status(), 406);
416
417 let mut accept = Accept::new();
418 accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?);
419 let err = accept.negotiate(&[mime::XML]).unwrap_err();
420 assert_eq!(err.status(), 406);
421 Ok(())
422 }
423
424 #[test]
425 fn negotiate_wildcard() -> crate::Result<()> {
426 let mut accept = Accept::new();
427 accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?);
428 accept.set_wildcard(true);
429
430 assert_eq!(accept.negotiate(&[mime::XML])?, mime::XML);
431 Ok(())
432 }
433}