mco_http/header/common/
preference_applied.rs

1use std::fmt;
2use crate::header::{Header, HeaderFormat, Preference};
3use crate::header::parsing::{from_comma_delimited, fmt_comma_delimited};
4
5/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
6///
7/// The `Preference-Applied` response header may be included within a
8/// response message as an indication as to which `Prefer` header tokens were
9/// honored by the server and applied to the processing of a request.
10///
11/// # ABNF
12/// ```plain
13/// Preference-Applied = "Preference-Applied" ":" 1#applied-pref
14/// applied-pref = token [ BWS "=" BWS word ]
15/// ```
16///
17/// # Example values
18/// * `respond-async`
19/// * `return=minimal`
20/// * `wait=30`
21///
22/// # Examples
23/// ```
24/// use mco_http::header::{Headers, PreferenceApplied, Preference};
25///
26/// let mut headers = Headers::new();
27/// headers.set(
28///     PreferenceApplied(vec![Preference::RespondAsync])
29/// );
30/// ```
31/// ```
32/// use mco_http::header::{Headers, PreferenceApplied, Preference};
33///
34/// let mut headers = Headers::new();
35/// headers.set(
36///     PreferenceApplied(vec![
37///         Preference::RespondAsync,
38///         Preference::ReturnRepresentation,
39///         Preference::Wait(10u32),
40///         Preference::Extension("foo".to_owned(),
41///                               "bar".to_owned(),
42///                               vec![]),
43///     ])
44/// );
45/// ```
46#[derive(PartialEq, Clone, Debug)]
47pub struct PreferenceApplied(pub Vec<Preference>);
48
49__mco_http__deref!(PreferenceApplied => Vec<Preference>);
50
51impl Header for PreferenceApplied {
52    fn header_name() -> &'static str {
53        "Preference-Applied"
54    }
55
56    fn parse_header(raw: &[Vec<u8>]) -> crate::Result<PreferenceApplied> {
57        let preferences = from_comma_delimited(raw)?;
58        if !preferences.is_empty() {
59            Ok(PreferenceApplied(preferences))
60        } else {
61            Err(crate::Error::Header)
62        }
63    }
64}
65
66impl HeaderFormat for PreferenceApplied {
67    fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        fmt::Display::fmt(self, f)
69    }
70}
71
72impl fmt::Display for PreferenceApplied {
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        //TODO: format this without allocating a Vec and cloning contents
75        let preferences: Vec<_> = self.0.iter().map(|pref| match pref {
76            // The spec ignores parameters in `Preferences-Applied`
77            &Preference::Extension(ref name, ref value, _) => Preference::Extension(
78              name.to_owned(),
79              value.to_owned(),
80              vec![]
81            ),
82            preference @ _ => preference.clone()
83        }).collect();
84        fmt_comma_delimited(f, &preferences)
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use crate::header::{HeaderFormat, Preference};
91    use super::*;
92
93    #[test]
94    fn test_format_ignore_parameters() {
95        assert_eq!(
96            format!("{}", &PreferenceApplied(vec![Preference::Extension(
97                "foo".to_owned(),
98                "bar".to_owned(),
99                vec![("bar".to_owned(), "foo".to_owned()), ("buz".to_owned(), "".to_owned())]
100            )]) as &(dyn HeaderFormat + Send + Sync)),
101            "foo=bar".to_owned()
102        );
103    }
104}
105
106bench_header!(normal,
107    PreferenceApplied, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] });