diff --git a/Cargo.toml b/Cargo.toml index 2fe9490..41f855e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,13 +16,16 @@ travis-ci = { repository = "nox/hyper_serde" } doctest = false [dependencies] -cookie = {version = "0.10", default-features = false} -hyper = "0.10" -mime = "0.2" +cookie = {version = "0.11", default-features = false} +http = "0.1" +hyper = { version = "0.12" } +mime = "0.3" serde = "1.0" serde_bytes = "0.10" time = "0.1" +headers-ext = "0.0.3" [dev-dependencies] serde_test = "1.0" time = "0.1" + diff --git a/src/lib.rs b/src/lib.rs index 92b3af0..a4220c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,14 +4,14 @@ //! The supported types are: //! //! * `cookie::Cookie` -//! * `hyper::header::ContentType` +//! * `headers_ext::ContentType` //! * `hyper::header::Headers` -//! * `hyper::http::RawStatus` -//! * `hyper::method::Method` +//! * `hyper::StatusCode` +//! * `hyper::Method` //! * `mime::Mime` //! * `time::Tm` //! -//! # How do I use a data type with a `Headers` member with Serde? +//! # How do I use a data type with a `HeaderMap` member with Serde? //! //! Use the serde attributes `deserialize_with` and `serialize_with`. //! @@ -19,11 +19,11 @@ //! struct MyStruct { //! #[serde(deserialize_with = "hyper_serde::deserialize", //! serialize_with = "hyper_serde::serialize")] -//! headers: Headers, +//! headers: HeaderMap, //! } //! ``` //! -//! # How do I encode a `Headers` value with `serde_json::to_string`? +//! # How do I encode a `HeaderMap` value with `serde_json::to_string`? //! //! Use the `Ser` wrapper. //! @@ -54,25 +54,30 @@ #![deny(unsafe_code)] extern crate cookie; +extern crate http; extern crate hyper; extern crate mime; extern crate serde; extern crate serde_bytes; extern crate time; +extern crate headers_ext; use cookie::Cookie; -use hyper::header::{ContentType, Headers}; -use hyper::http::RawStatus; -use hyper::method::Method; +use headers_ext::ContentType; +use hyper::StatusCode; +use hyper::header::{HeaderName, HeaderValue}; +use http::HeaderMap; +use hyper::Method; use mime::Mime; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_bytes::{ByteBuf, Bytes}; -use serde::de::{self, MapAccess, SeqAccess, Visitor}; +use serde::de::{self, MapAccess, SeqAccess, Visitor, Error}; use serde::ser::{SerializeMap, SerializeSeq}; use std::cmp; use std::fmt; use std::ops::{Deref, DerefMut}; use std::str; +use std::str::FromStr; use time::{Tm, strptime}; /// Deserialises a `T` value with a given deserializer. @@ -262,7 +267,7 @@ impl<'de> Deserialize<'de> for De { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - deserialize(deserializer).map(ContentType).map(De::new) + deserialize(deserializer).map(|v: mime::Mime| ContentType::from(v)).map(De::new) } } @@ -270,7 +275,7 @@ impl<'a> Serialize for Ser<'a, ContentType> { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - serialize(&self.v.0, serializer) + serialize(&mime::Mime::from(self.v.clone()), serializer) } } @@ -309,14 +314,15 @@ impl<'a, 'cookie> Serialize for Ser<'a, Cookie<'cookie>> { } } -impl<'de> Deserialize<'de> for De { + +impl<'de> Deserialize<'de> for De { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct HeadersVisitor; impl<'de> Visitor<'de> for HeadersVisitor { - type Value = De; + type Value = De; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a map from header names to header values") @@ -325,7 +331,7 @@ impl<'de> Deserialize<'de> for De { fn visit_unit(self) -> Result where E: de::Error, { - Ok(De::new(Headers::new())) + Ok(De::new(HeaderMap::new())) } fn visit_map(self, @@ -333,9 +339,11 @@ impl<'de> Deserialize<'de> for De { -> Result where V: MapAccess<'de>, { - let mut headers = Headers::new(); - while let Some((k, v)) = visitor.next_entry::()? { - headers.set_raw(k, v.0); + let mut headers = HeaderMap::new(); + while let Some((k, values)) = visitor.next_entry::()? { + for v in values.0.iter() { + headers.append(HeaderName::from_str(&k).map_err(V::Error::custom)?, HeaderValue::from_bytes(&v).map_err(V::Error::custom)?); + } } Ok(De::new(headers)) } @@ -383,7 +391,7 @@ impl<'de> Deserialize<'de> for De { } } -impl<'a> Serialize for Ser<'a, Headers> { +impl<'a> Serialize for Ser<'a, HeaderMap> { fn serialize(&self, serializer: S) -> Result where S: Serializer, { @@ -408,11 +416,10 @@ impl<'a> Serialize for Ser<'a, Headers> { } } - let mut serializer = serializer.serialize_map(Some(self.v.len()))?; - for header in self.v.iter() { - let name = header.name(); - let value = self.v.get_raw(name).unwrap(); - serializer.serialize_entry(name, &Value(value, self.pretty))?; + let mut serializer = serializer.serialize_map(Some(self.v.keys_len()))?; + for name in self.v.keys() { + let values = self.v.get_all(name); + serializer.serialize_entry(name.as_str(), &Value(&values.iter().map(|v| v.as_bytes().iter().cloned().collect()).collect::>>(), self.pretty))?; } serializer.end() } @@ -466,7 +473,7 @@ impl<'de> Deserialize<'de> for De { fn visit_str(self, v: &str) -> Result where E: de::Error, { - v.parse::().map(De::new).map_err(|()| { + v.parse::().map(De::new).map_err(|_| { E::custom("could not parse mime type") }) } @@ -484,20 +491,63 @@ impl<'a> Serialize for Ser<'a, Mime> { } } -impl<'de> Deserialize<'de> for De { +impl<'de> Deserialize<'de> for De { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let (code, reason) = Deserialize::deserialize(deserializer)?; - Ok(De::new(RawStatus(code, reason))) + let code = Deserialize::deserialize(deserializer)?; + Ok(De::new(StatusCode::from_u16(code).map_err(D::Error::custom)?)) + } +} + +impl<'a> Serialize for Ser<'a, StatusCode> { + fn serialize(&self, serializer: S) -> Result + where S: Serializer, + { + self.v.as_u16().serialize(serializer) } } -impl<'a> Serialize for Ser<'a, RawStatus> { +impl<'a> Serialize for Ser<'a, (StatusCode, String)> { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - (self.v.0, &self.v.1).serialize(serializer) + let mut serializer = serializer.serialize_seq(Some(2))?; + serializer.serialize_element(&Ser::new(&self.v.0))?; + serializer.serialize_element(&self.v.1)?; + serializer.end() + } +} + +impl<'de> Deserialize<'de> for De<(StatusCode, String)> { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de>, + { + Ok(De::new(deserializer.deserialize_seq(StatusVisitor)?)) + } +} + +struct StatusVisitor; + +impl<'de> Visitor<'de> for StatusVisitor { + type Value = (StatusCode, String); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "an array containing a status code and a reason string") + } + + fn visit_seq(self, mut visitor: V) -> Result + where V: SeqAccess<'de>, + { + + let code = visitor.next_element::()?.ok_or_else(|| + V::Error::custom("Can't find the status code") + )?; + let code = StatusCode::from_u16(code).map_err(V::Error::custom)?; + let reason = visitor.next_element::()?.ok_or_else(|| + V::Error::custom("Can't find the reason string") + )?; + Ok((code, reason)) } } diff --git a/tests/supported.rs b/tests/supported.rs index 6e2fa84..7a52e33 100644 --- a/tests/supported.rs +++ b/tests/supported.rs @@ -1,4 +1,6 @@ extern crate cookie; +extern crate headers_ext; +extern crate http; extern crate hyper; extern crate hyper_serde; extern crate mime; @@ -6,9 +8,9 @@ extern crate serde; extern crate time; use cookie::Cookie; -use hyper::header::{ContentType, Headers}; -use hyper::http::RawStatus; -use hyper::method::Method; +use http::header::HeaderMap; +use headers_ext::ContentType; +use hyper::{Method, StatusCode}; use hyper_serde::{De, Ser, Serde}; use mime::Mime; use serde::{Deserialize, Serialize}; @@ -25,9 +27,9 @@ fn is_supported() fn supported() { is_supported::(); is_supported::(); - is_supported::(); + is_supported::(); is_supported::(); is_supported::(); - is_supported::(); + is_supported::(); is_supported::(); } diff --git a/tests/tokens.rs b/tests/tokens.rs index b05c369..50b2e70 100644 --- a/tests/tokens.rs +++ b/tests/tokens.rs @@ -1,16 +1,18 @@ extern crate cookie; +extern crate headers_ext; +extern crate http; extern crate hyper; extern crate hyper_serde; -#[macro_use] extern crate mime; extern crate serde; extern crate serde_test; extern crate time; use cookie::Cookie; -use hyper::header::{ContentType, Headers}; -use hyper::http::RawStatus; -use hyper::method::Method; +use http::header::{self, HeaderMap, HeaderValue}; +use headers_ext::ContentType; +use http::StatusCode; +use hyper::Method; use hyper_serde::{De, Ser, deserialize}; use serde::Deserialize; use serde_test::{Deserializer, Token, assert_ser_tokens}; @@ -19,7 +21,7 @@ use time::Duration; #[test] fn test_content_type() { - let content_type = ContentType(mime!(Application / Json)); + let content_type = ContentType::from("Application/Json".parse::().unwrap()); let tokens = &[Token::Str("application/json")]; assert_ser_tokens(&Ser::new(&content_type), tokens); @@ -28,16 +30,18 @@ fn test_content_type() { #[test] fn test_cookie() { - let cookie = Cookie::build("Hello", "World!") + // Unfortunately we have to do the to_string().parse() dance here to avoid the object being a + // string with a bunch of indices in it which apparently is different from the exact same + // cookie but parsed as a bunch of strings. + let cookie: Cookie = Cookie::build("Hello", "World!") .max_age(Duration::seconds(42)) .domain("servo.org") .path("/") .secure(true) .http_only(false) - .finish(); + .finish().to_string().parse().unwrap(); - let tokens = &[Token::Str("Hello=World!; Secure; Path=/; \ - Domain=servo.org; Max-Age=42")]; + let tokens = &[Token::Str("Hello=World!; Secure; Path=/; Domain=servo.org; Max-Age=42")]; assert_ser_tokens(&Ser::new(&cookie), tokens); assert_de_tokens(&cookie, tokens); @@ -45,7 +49,7 @@ fn test_cookie() { #[test] fn test_headers_empty() { - let headers = Headers::new(); + let headers = HeaderMap::new(); let tokens = &[Token::Map { len: Some(0) }, Token::MapEnd]; @@ -55,19 +59,14 @@ fn test_headers_empty() { #[test] fn test_headers_not_empty() { - use hyper::header::Host; + let mut headers = HeaderMap::new(); + headers.insert(header::HOST, HeaderValue::from_static("baguette")); - let mut headers = Headers::new(); - headers.set(Host { - hostname: "baguette".to_owned(), - port: None, - }); - - // In Hyper 0.9, Headers is internally a HashMap and thus testing this + // In http, HeaderMap is internally a HashMap and thus testing this // with multiple headers is non-deterministic. let tokens = &[Token::Map{ len: Some(1) }, - Token::Str("Host"), + Token::Str("host"), Token::Seq{ len: Some(1) }, Token::Bytes(b"baguette"), Token::SeqEnd, @@ -77,7 +76,7 @@ fn test_headers_not_empty() { assert_de_tokens(&headers, tokens); let pretty = &[Token::Map{ len: Some(1) }, - Token::Str("Host"), + Token::Str("host"), Token::Seq{ len: Some(1) }, Token::Str("baguette"), Token::SeqEnd, @@ -89,7 +88,7 @@ fn test_headers_not_empty() { #[test] fn test_method() { - let method = Method::Put; + let method = Method::PUT; let tokens = &[Token::Str("PUT")]; assert_ser_tokens(&Ser::new(&method), tokens); @@ -98,13 +97,8 @@ fn test_method() { #[test] fn test_raw_status() { - use std::borrow::Cow; - - let raw_status = RawStatus(200, Cow::Borrowed("OK")); - let tokens = &[Token::Tuple{ len: 2 }, - Token::U16(200), - Token::Str("OK"), - Token::TupleEnd]; + let raw_status = StatusCode::from_u16(200).unwrap(); + let tokens = &[Token::U16(200)]; assert_ser_tokens(&Ser::new(&raw_status), tokens); assert_de_tokens(&raw_status, tokens);