http_types_rs/content/
accept.rs1use crate::headers::{HeaderName, HeaderValue, Headers, ACCEPT};
4use crate::mime::Mime;
5use crate::utils::sort_by_weight;
6use crate::{
7 content::{ContentType, MediaTypeProposal},
8 headers::Header,
9};
10use crate::{Error, StatusCode};
11
12use std::fmt::{self, Debug, Write};
13
14use std::slice;
15
16pub struct Accept {
52 wildcard: bool,
53 entries: Vec<MediaTypeProposal>,
54}
55
56impl Accept {
57 pub fn new() -> Self {
59 Self {
60 entries: vec![],
61 wildcard: false,
62 }
63 }
64
65 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
67 let mut entries = vec![];
68 let headers = match headers.as_ref().get(ACCEPT) {
69 Some(headers) => headers,
70 None => return Ok(None),
71 };
72
73 let mut wildcard = false;
74
75 for value in headers {
76 for part in value.as_str().trim().split(',') {
77 let part = part.trim();
78
79 if part.is_empty() {
81 continue;
82 } else if part == "*/*" {
83 wildcard = true;
84 continue;
85 }
86
87 let entry = MediaTypeProposal::from_str(part)?;
90 entries.push(entry);
91 }
92 }
93
94 Ok(Some(Self { wildcard, entries }))
95 }
96
97 pub fn push(&mut self, prop: impl Into<MediaTypeProposal>) {
99 self.entries.push(prop.into());
100 }
101
102 pub fn wildcard(&self) -> bool {
104 self.wildcard
105 }
106
107 pub fn set_wildcard(&mut self, wildcard: bool) {
109 self.wildcard = wildcard
110 }
111
112 pub fn sort(&mut self) {
118 sort_by_weight(&mut self.entries);
119 }
120
121 pub fn negotiate(&mut self, available: &[Mime]) -> crate::Result<ContentType> {
127 self.sort();
129
130 for accept in &self.entries {
132 if let Some(accept) = available.iter().find(|m| m.subset_eq(accept.media_type())) {
133 return Ok(accept.clone().into());
134 }
135 }
136
137 if self.wildcard {
139 if let Some(accept) = available.iter().next() {
140 return Ok(accept.clone().into());
141 }
142 }
143
144 let mut err = Error::new_adhoc("No suitable Content-Type found");
145 err.set_status(StatusCode::NotAcceptable);
146 Err(err)
147 }
148
149 pub fn iter(&self) -> Iter<'_> {
151 Iter { inner: self.entries.iter() }
152 }
153
154 pub fn iter_mut(&mut self) -> IterMut<'_> {
156 IterMut {
157 inner: self.entries.iter_mut(),
158 }
159 }
160}
161
162impl Header for Accept {
163 fn header_name(&self) -> HeaderName {
164 ACCEPT
165 }
166 fn header_value(&self) -> HeaderValue {
167 let mut output = String::new();
168 for (n, directive) in self.entries.iter().enumerate() {
169 let directive: HeaderValue = directive.clone().into();
170 match n {
171 0 => write!(output, "{}", directive).unwrap(),
172 _ => write!(output, ", {}", directive).unwrap(),
173 };
174 }
175
176 if self.wildcard {
177 match output.len() {
178 0 => write!(output, "*/*").unwrap(),
179 _ => write!(output, ", */*").unwrap(),
180 }
181 }
182
183 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
185 }
186}
187
188impl IntoIterator for Accept {
189 type Item = MediaTypeProposal;
190 type IntoIter = IntoIter;
191
192 #[inline]
193 fn into_iter(self) -> Self::IntoIter {
194 IntoIter {
195 inner: self.entries.into_iter(),
196 }
197 }
198}
199
200impl<'a> IntoIterator for &'a Accept {
201 type Item = &'a MediaTypeProposal;
202 type IntoIter = Iter<'a>;
203
204 #[inline]
205 fn into_iter(self) -> Self::IntoIter {
206 self.iter()
207 }
208}
209
210impl<'a> IntoIterator for &'a mut Accept {
211 type Item = &'a mut MediaTypeProposal;
212 type IntoIter = IterMut<'a>;
213
214 #[inline]
215 fn into_iter(self) -> Self::IntoIter {
216 self.iter_mut()
217 }
218}
219
220#[derive(Debug)]
222pub struct IntoIter {
223 inner: std::vec::IntoIter<MediaTypeProposal>,
224}
225
226impl Iterator for IntoIter {
227 type Item = MediaTypeProposal;
228
229 fn next(&mut self) -> Option<Self::Item> {
230 self.inner.next()
231 }
232
233 #[inline]
234 fn size_hint(&self) -> (usize, Option<usize>) {
235 self.inner.size_hint()
236 }
237}
238
239#[derive(Debug)]
241pub struct Iter<'a> {
242 inner: slice::Iter<'a, MediaTypeProposal>,
243}
244
245impl<'a> Iterator for Iter<'a> {
246 type Item = &'a MediaTypeProposal;
247
248 fn next(&mut self) -> Option<Self::Item> {
249 self.inner.next()
250 }
251
252 #[inline]
253 fn size_hint(&self) -> (usize, Option<usize>) {
254 self.inner.size_hint()
255 }
256}
257
258#[derive(Debug)]
260pub struct IterMut<'a> {
261 inner: slice::IterMut<'a, MediaTypeProposal>,
262}
263
264impl<'a> Iterator for IterMut<'a> {
265 type Item = &'a mut MediaTypeProposal;
266
267 fn next(&mut self) -> Option<Self::Item> {
268 self.inner.next()
269 }
270
271 #[inline]
272 fn size_hint(&self) -> (usize, Option<usize>) {
273 self.inner.size_hint()
274 }
275}
276
277impl Debug for Accept {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 let mut list = f.debug_list();
280 for directive in &self.entries {
281 list.entry(directive);
282 }
283 list.finish()
284 }
285}
286
287#[cfg(test)]
288mod test {
289 use super::*;
290 use crate::mime;
291 use crate::Response;
292
293 #[test]
294 fn smoke() -> crate::Result<()> {
295 let mut accept = Accept::new();
296 accept.push(mime::HTML);
297
298 let mut headers = Response::new(200);
299 accept.apply_header(&mut headers);
300
301 let accept = Accept::from_headers(headers)?.unwrap();
302 assert_eq!(accept.iter().next().unwrap(), mime::HTML);
303 Ok(())
304 }
305
306 #[test]
307 fn wildcard() -> crate::Result<()> {
308 let mut accept = Accept::new();
309 accept.set_wildcard(true);
310
311 let mut headers = Response::new(200);
312 accept.apply_header(&mut headers);
313
314 let accept = Accept::from_headers(headers)?.unwrap();
315 assert!(accept.wildcard());
316 Ok(())
317 }
318
319 #[test]
320 fn wildcard_and_header() -> crate::Result<()> {
321 let mut accept = Accept::new();
322 accept.push(mime::HTML);
323 accept.set_wildcard(true);
324
325 let mut headers = Response::new(200);
326 accept.apply_header(&mut headers);
327
328 let accept = Accept::from_headers(headers)?.unwrap();
329 assert!(accept.wildcard());
330 assert_eq!(accept.iter().next().unwrap(), mime::HTML);
331 Ok(())
332 }
333
334 #[test]
335 fn iter() -> crate::Result<()> {
336 let mut accept = Accept::new();
337 accept.push(mime::HTML);
338 accept.push(mime::XML);
339
340 let mut headers = Response::new(200);
341 accept.apply_header(&mut headers);
342
343 let accept = Accept::from_headers(headers)?.unwrap();
344 let mut accept = accept.iter();
345 assert_eq!(accept.next().unwrap(), mime::HTML);
346 assert_eq!(accept.next().unwrap(), mime::XML);
347 Ok(())
348 }
349
350 #[test]
351 fn reorder_based_on_weight() -> crate::Result<()> {
352 let mut accept = Accept::new();
353 accept.push(MediaTypeProposal::new(mime::HTML, Some(0.4))?);
354 accept.push(MediaTypeProposal::new(mime::XML, None)?);
355 accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?);
356
357 let mut headers = Response::new(200);
358 accept.apply_header(&mut headers);
359
360 let mut accept = Accept::from_headers(headers)?.unwrap();
361 accept.sort();
362 let mut accept = accept.iter();
363 assert_eq!(accept.next().unwrap(), mime::XML);
364 assert_eq!(accept.next().unwrap(), mime::PLAIN);
365 assert_eq!(accept.next().unwrap(), mime::HTML);
366 Ok(())
367 }
368
369 #[test]
370 fn reorder_based_on_weight_and_location() -> crate::Result<()> {
371 let mut accept = Accept::new();
372 accept.push(MediaTypeProposal::new(mime::HTML, None)?);
373 accept.push(MediaTypeProposal::new(mime::XML, None)?);
374 accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?);
375
376 let mut res = Response::new(200);
377 accept.apply_header(&mut res);
378
379 let mut accept = Accept::from_headers(res)?.unwrap();
380 accept.sort();
381 let mut accept = accept.iter();
382 assert_eq!(accept.next().unwrap(), mime::XML);
383 assert_eq!(accept.next().unwrap(), mime::HTML);
384 assert_eq!(accept.next().unwrap(), mime::PLAIN);
385 Ok(())
386 }
387
388 #[test]
389 fn negotiate() -> crate::Result<()> {
390 let mut accept = Accept::new();
391 accept.push(MediaTypeProposal::new(mime::HTML, Some(0.4))?);
392 accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?);
393 accept.push(MediaTypeProposal::new(mime::XML, None)?);
394
395 assert_eq!(accept.negotiate(&[mime::HTML, mime::XML])?, mime::XML);
396 Ok(())
397 }
398
399 #[test]
400 fn negotiate_not_acceptable() -> crate::Result<()> {
401 let mut accept = Accept::new();
402 let err = accept.negotiate(&[mime::JSON]).unwrap_err();
403 assert_eq!(err.status(), 406);
404
405 let mut accept = Accept::new();
406 accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?);
407 let err = accept.negotiate(&[mime::XML]).unwrap_err();
408 assert_eq!(err.status(), 406);
409 Ok(())
410 }
411
412 #[test]
413 fn negotiate_wildcard() -> crate::Result<()> {
414 let mut accept = Accept::new();
415 accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?);
416 accept.set_wildcard(true);
417
418 assert_eq!(accept.negotiate(&[mime::XML])?, mime::XML);
419 Ok(())
420 }
421
422 #[test]
423 fn negotiate_missing_encoding() -> crate::Result<()> {
424 let mime_html = "text/html".parse::<Mime>()?;
425
426 let mut browser_accept = Accept::new();
427 browser_accept.push(MediaTypeProposal::new(mime_html, None)?);
428
429 let acceptable = &[mime::HTML];
430
431 let content_type = browser_accept.negotiate(acceptable);
432
433 assert!(content_type.is_ok(), "server is expected to return HTML content");
434 Ok(())
435 }
436
437 #[test]
438 fn parse() -> crate::Result<()> {
439 let mut headers = Headers::new();
440 headers.insert("Accept", "application/json; q=0.8,*/*")?;
441 let accept = Accept::from_headers(headers)?.unwrap();
442
443 assert!(accept.wildcard());
444 assert_eq!(
445 accept.into_iter().collect::<Vec<_>>(),
446 vec![MediaTypeProposal::new(mime::JSON, Some(0.8))?]
447 );
448 Ok(())
449 }
450
451 #[test]
452 fn serialize() -> crate::Result<()> {
453 let mut accept = Accept::new();
454 accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?);
455 accept.set_wildcard(true);
456
457 assert_eq!(accept.header_value().as_str(), "application/json;q=0.800, */*");
458 Ok(())
459 }
460}