Skip to content

Commit

Permalink
Add impl Serialize and Deserialize for Option<Url>
Browse files Browse the repository at this point in the history
This commit is adding impl Serialize and Deserialize for Option<Url>
which is intended to be used with serde serliaze_with and
deserialize_with for derived ser/deser when an url field is optional.

This commit is also adding some tests for derived Serialize and
Deserialize via serde serialize_with and deserialize_with, as
custom_derive is stable since rust 1.15.
  • Loading branch information
mhristache committed Feb 19, 2017
1 parent c7254e5 commit 599b97d
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 2 deletions.
3 changes: 2 additions & 1 deletion url_serde/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

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

description = "Serde support for URL types"
Expand All @@ -17,6 +17,7 @@ url = "1.0.0"

[dev-dependencies]
serde_json = "0.9.0"
serde_derive = "0.9.0"

[lib]
doctest = false
124 changes: 123 additions & 1 deletion url_serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ ipc::channel::<Serde<Url>>()
extern crate serde;
extern crate url;

#[cfg(test)]
#[macro_use]
extern crate serde_derive;

use std::cmp::PartialEq;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::error::Error;
use serde::{Deserialize, Serialize, Serializer, Deserializer};
Expand Down Expand Up @@ -95,6 +98,18 @@ impl<'a> Serialize for Ser<'a, Url> {
}


/// Serializes this Option<URL> into a `serde` stream.
impl<'a> Serialize for Ser<'a, Option<Url>> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
if let Some(url) = self.0.as_ref() {
serializer.serialize_some(url.as_str())
} else {
serializer.serialize_none()
}
}
}


/// Deserialises a `T` value with a given deserializer.
///
/// This is useful to deserialize Url types used in structure fields or
Expand Down Expand Up @@ -135,6 +150,21 @@ impl Deserialize for De<Url> {
}


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

}
}

/// A convenience wrapper to be used as a type parameter, for example when
/// a `Vec<T>` or an `HashMap<K, V>` need to be passed to serde.
#[derive(Clone, Eq, Hash, PartialEq)]
Expand Down Expand Up @@ -217,3 +247,95 @@ fn test_ser_de_url() {
let new_url: Url = serde_json::from_str(&s).map(De::into_inner).unwrap();
assert_eq!(url, new_url);
}


#[test]
fn test_derive_deserialize_with_for_url() {
extern crate serde_json;

#[derive(Deserialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(deserialize_with = "deserialize", rename = "_url_")]
url: Url
}

let url_str = "http://www.test.com/foo/bar?$param=bazz";

let expected = Test {
url: Url::parse(url_str).unwrap()
};
let json_string = format!(r#"{{"_url_": "{}"}}"#, url_str);
let got: Test = serde_json::from_str(&json_string).unwrap();
assert_eq!(expected, got);

}

#[test]
fn test_derive_deserialize_with_for_option_url() {
extern crate serde_json;

#[derive(Deserialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(deserialize_with = "deserialize", rename = "_url_")]
url: Option<Url>
}

let url_str = "http://www.test.com/foo/bar?$param=bazz";

let expected = Test {
url: Some(Url::parse(url_str).unwrap())
};
let json_string = format!(r#"{{"_url_": "{}"}}"#, url_str);
let got: Test = serde_json::from_str(&json_string).unwrap();
assert_eq!(expected, got);

let expected = Test {
url: None
};
let json_string = r#"{"_url_": null}"#;
let got: Test = serde_json::from_str(&json_string).unwrap();
assert_eq!(expected, got);
}


#[test]
fn test_derive_serialize_with_for_url() {
extern crate serde_json;

#[derive(Serialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(serialize_with = "serialize", rename = "_url_")]
url: Url
}

let url_str = "http://www.test.com/foo/bar?$param=bazz";

let expected = format!(r#"{{"_url_":"{}"}}"#, url_str);
let input = Test {url: Url::parse(url_str).unwrap()};
let got = serde_json::to_string(&input).unwrap();
assert_eq!(expected, got);
}


#[test]
fn test_derive_serialize_with_for_option_url() {
extern crate serde_json;

#[derive(Serialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(serialize_with = "serialize", rename = "_url_")]
url: Option<Url>
}

let url_str = "http://www.test.com/foo/bar?$param=bazz";

let expected = format!(r#"{{"_url_":"{}"}}"#, url_str);
let input = Test {url: Some(Url::parse(url_str).unwrap())};
let got = serde_json::to_string(&input).unwrap();
assert_eq!(expected, got);

let expected = format!(r#"{{"_url_":null}}"#);
let input = Test {url: None};
let got = serde_json::to_string(&input).unwrap();
assert_eq!(expected, got);
}

0 comments on commit 599b97d

Please sign in to comment.