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