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