1use std::fmt;
2use std::str::FromStr;
3use crate::header::{Header, HeaderFormat};
4use crate::header::parsing::{from_comma_delimited, fmt_comma_delimited};
5
6#[derive(PartialEq, Clone, Debug)]
50pub struct Prefer(pub Vec<Preference>);
51
52__hyper__deref!(Prefer => Vec<Preference>);
53
54impl Header for Prefer {
55 fn header_name() -> &'static str {
56 "Prefer"
57 }
58
59 fn parse_header(raw: &[Vec<u8>]) -> crate::Result<Prefer> {
60 let preferences = r#try!(from_comma_delimited(raw));
61 if !preferences.is_empty() {
62 Ok(Prefer(preferences))
63 } else {
64 Err(crate::Error::Header)
65 }
66 }
67}
68
69impl HeaderFormat for Prefer {
70 fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 fmt::Display::fmt(self, f)
72 }
73}
74
75impl fmt::Display for Prefer {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 fmt_comma_delimited(f, &self[..])
78 }
79}
80
81#[derive(PartialEq, Clone, Debug)]
83pub enum Preference {
84 RespondAsync,
86 ReturnRepresentation,
88 ReturnMinimal,
90 HandlingStrict,
92 HandlingLeniant,
94 Wait(u32),
96
97 Extension(String, String, Vec<(String, String)>)
100}
101
102impl fmt::Display for Preference {
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 use self::Preference::*;
105 fmt::Display::fmt(match *self {
106 RespondAsync => "respond-async",
107 ReturnRepresentation => "return=representation",
108 ReturnMinimal => "return=minimal",
109 HandlingStrict => "handling=strict",
110 HandlingLeniant => "handling=leniant",
111
112 Wait(secs) => return write!(f, "wait={}", secs),
113
114 Extension(ref name, ref value, ref params) => {
115 r#try!(write!(f, "{}", name));
116 if value != "" { r#try!(write!(f, "={}", value)); }
117 if params.len() > 0 {
118 for &(ref name, ref value) in params {
119 r#try!(write!(f, "; {}", name));
120 if value != "" { r#try!(write!(f, "={}", value)); }
121 }
122 }
123 return Ok(());
124 }
125 }, f)
126 }
127}
128
129impl FromStr for Preference {
130 type Err = Option<<u32 as FromStr>::Err>;
131 fn from_str(s: &str) -> Result<Preference, Option<<u32 as FromStr>::Err>> {
132 use self::Preference::*;
133 let mut params = s.split(';').map(|p| {
134 let mut param = p.splitn(2, '=');
135 match (param.next(), param.next()) {
136 (Some(name), Some(value)) => (name.trim(), value.trim().trim_matches('"')),
137 (Some(name), None) => (name.trim(), ""),
138 _ => { unreachable!(); }
143 }
144 });
145 match params.nth(0) {
146 Some(param) => {
147 let rest: Vec<(String, String)> = params.map(|(l, r)| (l.to_owned(), r.to_owned())).collect();
148 match param {
149 ("respond-async", "") => if rest.len() == 0 { Ok(RespondAsync) } else { Err(None) },
150 ("return", "representation") => if rest.len() == 0 { Ok(ReturnRepresentation) } else { Err(None) },
151 ("return", "minimal") => if rest.len() == 0 { Ok(ReturnMinimal) } else { Err(None) },
152 ("handling", "strict") => if rest.len() == 0 { Ok(HandlingStrict) } else { Err(None) },
153 ("handling", "leniant") => if rest.len() == 0 { Ok(HandlingLeniant) } else { Err(None) },
154 ("wait", secs) => if rest.len() == 0 { secs.parse().map(Wait).map_err(Some) } else { Err(None) },
155 (left, right) => Ok(Extension(left.to_owned(), right.to_owned(), rest))
156 }
157 },
158 None => Err(None)
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::header::Header;
166 use super::*;
167
168 #[test]
169 fn test_parse_multiple_headers() {
170 let prefer = Header::parse_header(&[b"respond-async, return=representation".to_vec()]);
171 assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync,
172 Preference::ReturnRepresentation])))
173 }
174
175 #[test]
176 fn test_parse_argument() {
177 let prefer = Header::parse_header(&[b"wait=100, handling=leniant, respond-async".to_vec()]);
178 assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100),
179 Preference::HandlingLeniant,
180 Preference::RespondAsync])))
181 }
182
183 #[test]
184 fn test_parse_quote_form() {
185 let prefer = Header::parse_header(&[b"wait=\"200\", handling=\"strict\"".to_vec()]);
186 assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200),
187 Preference::HandlingStrict])))
188 }
189
190 #[test]
191 fn test_parse_extension() {
192 let prefer = Header::parse_header(&[b"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".to_vec()]);
193 assert_eq!(prefer.ok(), Some(Prefer(vec![
194 Preference::Extension("foo".to_owned(), "".to_owned(), vec![]),
195 Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]),
196 Preference::Extension("baz".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned()), ("bar".to_owned(), "baz".to_owned())]),
197 Preference::Extension("bux".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned())]),
198 Preference::Extension("buz".to_owned(), "some parameter".to_owned(), vec![])])))
199 }
200
201 #[test]
202 fn test_fail_with_args() {
203 let prefer: crate::Result<Prefer> = Header::parse_header(&[b"respond-async; foo=bar".to_vec()]);
204 assert_eq!(prefer.ok(), None);
205 }
206}
207
208bench_header!(normal,
209 Prefer, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] });