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