forked from servo/rust-url
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add url_serde crate with ser/de support for Url type
- Loading branch information
1 parent
bb1100b
commit 3feb089
Showing
5 changed files
with
248 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |