Skip to content

Commit

Permalink
Add url_serde crate with ser/de support for Url type
Browse files Browse the repository at this point in the history
  • Loading branch information
mhristache committed Jan 27, 2017
1 parent bb1100b commit 3feb089
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ keywords = ["url", "parser"]
license = "MIT/Apache-2.0"

[workspace]
members = [".", "idna"]
members = [".", "idna", "url_serde"]

[[test]]
name = "unit"
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
test:
cargo test --features "query_encoding serde rustc-serialize heapsize"
(cd idna && cargo test)
(cd url_serde && cargo test)

.PHONY: test
22 changes: 22 additions & 0 deletions url_serde/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]

name = "url_serde"
version = "0.1.0"
authors = ["The rust-url developers"]

description = "Serde support for URL types"
documentation = "https://docs.rs/url_serde/"
repository = "https://github.com/servo/rust-url"
readme = "README.md"
keywords = ["url", "serde"]
license = "MIT/Apache-2.0"

[dependencies]
serde = "0.9.0"
url = "1.0.0"

[dev-dependencies]
serde_json = "0.9.0"

[lib]
doctest = false
9 changes: 9 additions & 0 deletions url_serde/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Serde support for rust-url types
================================

This crate provides wrappers and convenience functions to make `rust-url` and `serde`
work hand in hand.

This crate supports `serde 0.9.0` or newer. Older versions of `serde` are natively supported by `rust-url` crate directly.

For more details, see the crate [documentation](https://docs.rs/url_serde/).
215 changes: 215 additions & 0 deletions url_serde/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*!
This crate provides wrappers and convenience functions to make rust-url
and Serde work hand in hand.
The supported types are:
* `url::Url`
# How do I use a data type with a `Url` member with Serde?
Use the serde attributes `deserialize_with` and `serialize_with`.
```
#[derive(serde::Serialize, serde::Deserialize)]
struct MyStruct {
#[serde(deserialize_with = "url_serde::deserialize",
serialize_with = "url_serde::serialize")]
url: Url,
}
```
# How do I encode a `Url` value with `serde_json::to_string`?
Use the `Ser` wrapper.
```
serde_json::to_string(&Ser::new(&url))
```
# How do I decode a `Url` value with `serde_json::parse`?
Use the `De` wrapper.
```
serde_json::from_str(r"http:://www.rust-lang.org").map(De::into_inner)
```
# How do I send `Url` values as part of an IPC channel?
Use the `Serde` wrapper. It implements `Deref` and `DerefMut` for convenience.
```
ipc::channel::<Serde<Url>>()
```
*/

#![deny(missing_docs)]
#![deny(unsafe_code)]

extern crate serde;
extern crate url;

use std::cmp::PartialEq;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::error::Error;
use serde::{Deserialize, Serialize, Serializer, Deserializer};
use url::{Url};


/// Serialises `value` with a given serializer.
///
/// This is useful to serialize `rust-url` types used in structure fields or
/// tuple members with `#[serde(serialize_with = "url_serde::serialize")]`.
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer, for<'a> Ser<'a, T>: Serialize
{
Ser::new(value).serialize(serializer)
}

/// A wrapper to serialize `rust-url` types.
///
/// This is useful with functions such as `serde_json::to_string`.
///
/// Values of this type can only be passed to the `serde::Serialize` trait.
#[derive(Debug)]
pub struct Ser<'a, T: 'a>(&'a T);

impl<'a, T> Ser<'a, T> where Ser<'a, T>: Serialize {
/// Returns a new `Ser` wrapper.
#[inline(always)]
pub fn new(value: &'a T) -> Self {
Ser(value)
}
}


/// Serializes this URL into a `serde` stream.
impl<'a> Serialize for Ser<'a, Url> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
serializer.serialize_str(self.0.as_str())
}
}


/// Deserialises a `T` value with a given deserializer.
///
/// This is useful to deserialize Url types used in structure fields or
/// tuple members with `#[serde(deserialize_with = "url_serde::deserialize")]`.
pub fn deserialize<T, D>(deserializer: D) -> Result<T, D::Error>
where D: Deserializer, De<T>: Deserialize
{
De::deserialize(deserializer).map(De::into_inner)
}


/// A wrapper to deserialize `rust-url` types.
///
/// This is useful with functions such as `serde_json::from_str`.
///
/// Values of this type can only be obtained through
/// the `serde::Deserialize` trait.
#[derive(Debug)]
pub struct De<T>(T);

impl<T> De<T> where De<T>: serde::Deserialize {
/// Consumes this wrapper, returning the deserialized value.
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}


/// Deserializes this URL from a `serde` stream.
impl Deserialize for De<Url> {
fn deserialize<D>(deserializer: D) -> Result<De<Url>, D::Error> where D: Deserializer {
let string_representation: String = Deserialize::deserialize(deserializer)?;
Url::parse(&string_representation).map(De).map_err(|err| {
serde::de::Error::custom(err.description())
})
}
}


/// A convenience wrapper to be used as a type parameter, for example when
/// a `Vec<T>` need to be passed to serde.
#[derive(Clone, PartialEq)]
pub struct Serde<T>(pub T)
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize;

impl<T> Serde<T>
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
/// Consumes this wrapper, returning the inner value.
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}

impl<T> fmt::Debug for Serde<T>
where T: fmt::Debug, De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0.fmt(formatter)
}
}

impl<T> Deref for Serde<T>
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
type Target = T;

fn deref(&self) -> &T {
&self.0
}
}

impl<T> DerefMut for Serde<T>
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}

impl<T: PartialEq> PartialEq<T> for Serde<T>
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
fn eq(&self, other: &T) -> bool {
self.0 == *other
}
}

impl<T> Deserialize for Serde<T>
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer
{
De::deserialize(deserializer).map(De::into_inner).map(Serde)
}
}

impl<T> Serialize for Serde<T>
where De<T>: Deserialize, for<'a> Ser<'a, T>: Serialize
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
Ser(&self.0).serialize(serializer)
}
}


#[test]
fn test_ser_de_url() {
extern crate serde_json;
let url = Url::parse("http://www.test.com/foo/bar?$param=bazz").unwrap();
let s = serde_json::to_string(&Ser::new(&url)).unwrap();
let new_url: Url = serde_json::from_str(&s).map(De::into_inner).unwrap();
assert_eq!(url, new_url);
}

0 comments on commit 3feb089

Please sign in to comment.