use std::fmt;
use std::str::FromStr;
use header::{Header, Raw};
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
#[derive(PartialEq, Clone, Debug)]
pub struct Prefer(pub Vec<Preference>);
__hyper__deref!(Prefer => Vec<Preference>);
impl Header for Prefer {
fn header_name() -> &'static str {
static NAME: &'static str = "Prefer";
NAME
}
fn parse_header(raw: &Raw) -> ::Result<Prefer> {
let preferences = try!(from_comma_delimited(raw));
if !preferences.is_empty() {
Ok(Prefer(preferences))
} else {
Err(::Error::Header)
}
}
fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
f.fmt_line(self)
}
}
impl fmt::Display for Prefer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_comma_delimited(f, &self[..])
}
}
#[derive(PartialEq, Clone, Debug)]
pub enum Preference {
RespondAsync,
ReturnRepresentation,
ReturnMinimal,
HandlingStrict,
HandlingLenient,
Wait(u32),
Extension(String, String, Vec<(String, String)>)
}
impl fmt::Display for Preference {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Preference::*;
fmt::Display::fmt(match *self {
RespondAsync => "respond-async",
ReturnRepresentation => "return=representation",
ReturnMinimal => "return=minimal",
HandlingStrict => "handling=strict",
HandlingLenient => "handling=lenient",
Wait(secs) => return write!(f, "wait={}", secs),
Extension(ref name, ref value, ref params) => {
try!(write!(f, "{}", name));
if value != "" { try!(write!(f, "={}", value)); }
if !params.is_empty() {
for &(ref name, ref value) in params {
try!(write!(f, "; {}", name));
if value != "" { try!(write!(f, "={}", value)); }
}
}
return Ok(());
}
}, f)
}
}
impl FromStr for Preference {
type Err = Option<<u32 as FromStr>::Err>;
fn from_str(s: &str) -> Result<Preference, Option<<u32 as FromStr>::Err>> {
use self::Preference::*;
let mut params = s.split(';').map(|p| {
let mut param = p.splitn(2, '=');
match (param.next(), param.next()) {
(Some(name), Some(value)) => (name.trim(), value.trim().trim_matches('"')),
(Some(name), None) => (name.trim(), ""),
_ => { unreachable!(); }
}
});
match params.nth(0) {
Some(param) => {
let rest: Vec<(String, String)> = params.map(|(l, r)| (l.to_owned(), r.to_owned())).collect();
match param {
("respond-async", "") => if rest.is_empty() { Ok(RespondAsync) } else { Err(None) },
("return", "representation") => if rest.is_empty() { Ok(ReturnRepresentation) } else { Err(None) },
("return", "minimal") => if rest.is_empty() { Ok(ReturnMinimal) } else { Err(None) },
("handling", "strict") => if rest.is_empty() { Ok(HandlingStrict) } else { Err(None) },
("handling", "lenient") => if rest.is_empty() { Ok(HandlingLenient) } else { Err(None) },
("wait", secs) => if rest.is_empty() { secs.parse().map(Wait).map_err(Some) } else { Err(None) },
(left, right) => Ok(Extension(left.to_owned(), right.to_owned(), rest))
}
},
None => Err(None)
}
}
}
#[cfg(test)]
mod tests {
use header::Header;
use super::*;
#[test]
fn test_parse_multiple_headers() {
let prefer = Header::parse_header(&"respond-async, return=representation".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync,
Preference::ReturnRepresentation])))
}
#[test]
fn test_parse_argument() {
let prefer = Header::parse_header(&"wait=100, handling=lenient, respond-async".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100),
Preference::HandlingLenient,
Preference::RespondAsync])))
}
#[test]
fn test_parse_quote_form() {
let prefer = Header::parse_header(&"wait=\"200\", handling=\"strict\"".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200),
Preference::HandlingStrict])))
}
#[test]
fn test_parse_extension() {
let prefer = Header::parse_header(&"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".into());
assert_eq!(prefer.ok(), Some(Prefer(vec![
Preference::Extension("foo".to_owned(), "".to_owned(), vec![]),
Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]),
Preference::Extension("baz".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned()), ("bar".to_owned(), "baz".to_owned())]),
Preference::Extension("bux".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned())]),
Preference::Extension("buz".to_owned(), "some parameter".to_owned(), vec![])])))
}
#[test]
fn test_fail_with_args() {
let prefer: ::Result<Prefer> = Header::parse_header(&"respond-async; foo=bar".into());
assert_eq!(prefer.ok(), None);
}
}
bench_header!(normal,
Prefer, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] });