Skip to content

Add support for Serde 1.0 #327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ matches = "0.1"
percent-encoding = { version = "1.0.0", path = "./percent_encoding" }
rustc-serialize = {version = "0.3", optional = true}
serde = {version = ">=0.6.1, <0.9", optional = true}
serde1 = {version = "1.0", optional = true}

[[bench]]
name = "parse_url"
Expand Down
56 changes: 0 additions & 56 deletions src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,6 @@ pub enum HostInternal {
#[cfg(feature = "heapsize")]
known_heap_size!(0, HostInternal);

#[cfg(feature="serde")]
impl ::serde::Serialize for HostInternal {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: ::serde::Serializer {
// This doesn’t use `derive` because that involves
// large dependencies (that take a long time to build), and
// either Macros 1.1 which are not stable yet or a cumbersome build script.
//
// Implementing `Serializer` correctly for an enum is tricky,
// so let’s use existing enums that already do.
use std::net::IpAddr;
match *self {
HostInternal::None => None,
HostInternal::Domain => Some(None),
HostInternal::Ipv4(addr) => Some(Some(IpAddr::V4(addr))),
HostInternal::Ipv6(addr) => Some(Some(IpAddr::V6(addr))),
}.serialize(serializer)
}
}

#[cfg(feature="serde")]
impl ::serde::Deserialize for HostInternal {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: ::serde::Deserializer {
use std::net::IpAddr;
Ok(match ::serde::Deserialize::deserialize(deserializer)? {
None => HostInternal::None,
Some(None) => HostInternal::Domain,
Some(Some(IpAddr::V4(addr))) => HostInternal::Ipv4(addr),
Some(Some(IpAddr::V6(addr))) => HostInternal::Ipv6(addr),
})
}
}

impl<S> From<Host<S>> for HostInternal {
fn from(host: Host<S>) -> HostInternal {
match host {
Expand Down Expand Up @@ -91,30 +59,6 @@ pub enum Host<S=String> {
Ipv6(Ipv6Addr),
}

#[cfg(feature="serde")]
impl<S: ::serde::Serialize> ::serde::Serialize for Host<S> {
fn serialize<R>(&self, serializer: &mut R) -> Result<(), R::Error> where R: ::serde::Serializer {
use std::net::IpAddr;
match *self {
Host::Domain(ref s) => Ok(s),
Host::Ipv4(addr) => Err(IpAddr::V4(addr)),
Host::Ipv6(addr) => Err(IpAddr::V6(addr)),
}.serialize(serializer)
}
}

#[cfg(feature="serde")]
impl<S: ::serde::Deserialize> ::serde::Deserialize for Host<S> {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: ::serde::Deserializer {
use std::net::IpAddr;
Ok(match ::serde::Deserialize::deserialize(deserializer)? {
Ok(s) => Host::Domain(s),
Err(IpAddr::V4(addr)) => Host::Ipv4(addr),
Err(IpAddr::V6(addr)) => Host::Ipv6(addr),
})
}
}

#[cfg(feature = "heapsize")]
impl<S: HeapSizeOf> HeapSizeOf for Host<S> {
fn heap_size_of_children(&self) -> usize {
Expand Down
89 changes: 89 additions & 0 deletions src/legacy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::error::Error;
use std::net::IpAddr;

use legacy_serde::{de, Deserialize, Deserializer, Serialize, Serializer};

use host::HostInternal;
use Host;
use Url;

/// Serializes this URL into a `serde` stream.
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
impl Serialize for Url {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}

/// Deserializes this URL from a `serde` stream.
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
impl Deserialize for Url {
fn deserialize<D>(deserializer: &mut D) -> Result<Url, D::Error>
where
D: Deserializer,
{
let string_representation = String::deserialize(deserializer)?;
Url::parse(&string_representation).map_err(|err| {
de::Error::invalid_value(err.description())
})
}
}

impl Serialize for HostInternal {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where
S: Serializer,
{
match *self {
HostInternal::None => None,
HostInternal::Domain => Some(None),
HostInternal::Ipv4(addr) => Some(Some(IpAddr::V4(addr))),
HostInternal::Ipv6(addr) => Some(Some(IpAddr::V6(addr))),
}.serialize(serializer)
}
}

impl Deserialize for HostInternal {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where
D: Deserializer,
{
Ok(match Deserialize::deserialize(deserializer)? {
None => HostInternal::None,
Some(None) => HostInternal::Domain,
Some(Some(IpAddr::V4(addr))) => HostInternal::Ipv4(addr),
Some(Some(IpAddr::V6(addr))) => HostInternal::Ipv6(addr),
})
}
}

impl<S: Serialize> Serialize for Host<S> {
fn serialize<R>(&self, serializer: &mut R) -> Result<(), R::Error>
where
R: Serializer,
{
match *self {
Host::Domain(ref s) => Ok(s),
Host::Ipv4(addr) => Err(IpAddr::V4(addr)),
Host::Ipv6(addr) => Err(IpAddr::V6(addr)),
}.serialize(serializer)
}
}

impl<S: Deserialize> Deserialize for Host<S> {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where
D: Deserializer,
{
Ok(match Deserialize::deserialize(deserializer)? {
Ok(s) => Host::Domain(s),
Err(IpAddr::V4(addr)) => Host::Ipv4(addr),
Err(IpAddr::V6(addr)) => Host::Ipv6(addr),
})
}
}
40 changes: 11 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ assert_eq!(css_url.as_str(), "http://servo.github.io/rust-url/main.css");

#[cfg(feature="rustc-serialize")] extern crate rustc_serialize;
#[macro_use] extern crate matches;
#[cfg(feature="serde")] extern crate serde;
#[cfg(feature="serde")] extern crate serde as legacy_serde;
#[cfg(feature="serde1")] extern crate serde1;
#[cfg(feature="heapsize")] #[macro_use] extern crate heapsize;

pub extern crate idna;
Expand All @@ -123,7 +124,6 @@ use percent_encoding::{PATH_SEGMENT_ENCODE_SET, USERINFO_ENCODE_SET,
percent_encode, percent_decode, utf8_percent_encode};
use std::borrow::Borrow;
use std::cmp;
#[cfg(feature = "serde")] use std::error::Error;
use std::fmt::{self, Write, Debug, Formatter};
use std::hash;
use std::io;
Expand All @@ -146,6 +146,11 @@ mod path_segments;
mod parser;
mod slicing;

#[cfg(feature = "serde")]
mod legacy;
#[cfg(feature = "serde1")]
mod serde;

pub mod form_urlencoded;
#[doc(hidden)] pub mod quirks;

Expand Down Expand Up @@ -2028,8 +2033,8 @@ impl Url {
/// This method is only available if the `serde` Cargo feature is enabled.
#[cfg(feature = "serde")]
#[deny(unused)]
pub fn serialize_internal<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
use serde::Serialize;
pub fn serialize_internal<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: legacy_serde::Serializer {
use legacy_serde::Serialize;
// Destructuring first lets us ensure that adding or removing fields forces this method
// to be updated
let Url { ref serialization, ref scheme_end,
Expand All @@ -2050,8 +2055,8 @@ impl Url {
/// This method is only available if the `serde` Cargo feature is enabled.
#[cfg(feature = "serde")]
#[deny(unused)]
pub fn deserialize_internal<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
use serde::{Deserialize, Error};
pub fn deserialize_internal<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: legacy_serde::Deserializer {
use legacy_serde::{Deserialize, Error};
let (serialization, scheme_end, username_end,
host_start, host_end, host, port, path_start,
query_start, fragment_start) = Deserialize::deserialize(deserializer)?;
Expand Down Expand Up @@ -2242,29 +2247,6 @@ impl rustc_serialize::Decodable for Url {
}
}

/// Serializes this URL into a `serde` stream.
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
#[cfg(feature="serde")]
impl serde::Serialize for Url {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
serializer.serialize_str(self.as_str())
}
}

/// Deserializes this URL from a `serde` stream.
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
#[cfg(feature="serde")]
impl serde::Deserialize for Url {
fn deserialize<D>(deserializer: &mut D) -> Result<Url, D::Error> where D: serde::Deserializer {
let string_representation: String = serde::Deserialize::deserialize(deserializer)?;
Url::parse(&string_representation).map_err(|err| {
serde::Error::invalid_value(err.description())
})
}
}

#[cfg(any(unix, target_os = "redox"))]
fn path_to_file_url_segments(path: &Path, serialization: &mut String)
-> Result<(u32, HostInternal), ()> {
Expand Down
45 changes: 45 additions & 0 deletions src/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::fmt;

use serde1::de::{self, Deserialize, Deserializer, Visitor};
use serde1::ser::{Serialize, Serializer};

use Url;

/// This implementation is only available if the `serde1` Cargo feature is
/// enabled.
impl Serialize for Url {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}

/// This implementation is only available if the `serde1` Cargo feature is
/// enabled.
impl<'de> Deserialize<'de> for Url {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct UrlVisitor;

impl<'de> Visitor<'de> for UrlVisitor {
type Value = Url;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a URL string")
}

fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
s.parse().map_err(de::Error::custom)
}
}

deserializer.deserialize_str(UrlVisitor)
}
}