From 599b97de8d9bc3738620324555f2c56c18ca7794 Mon Sep 17 00:00:00 2001 From: Maximilian Hristache Date: Sun, 19 Feb 2017 11:59:31 +0100 Subject: [PATCH] Add impl Serialize and Deserialize for Option This commit is adding impl Serialize and Deserialize for Option 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. --- url_serde/Cargo.toml | 3 +- url_serde/src/lib.rs | 124 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/url_serde/Cargo.toml b/url_serde/Cargo.toml index 2c8570aa..9459de60 100644 --- a/url_serde/Cargo.toml +++ b/url_serde/Cargo.toml @@ -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" @@ -17,6 +17,7 @@ url = "1.0.0" [dev-dependencies] serde_json = "0.9.0" +serde_derive = "0.9.0" [lib] doctest = false diff --git a/url_serde/src/lib.rs b/url_serde/src/lib.rs index f0a0feb9..b2c7f0ce 100644 --- a/url_serde/src/lib.rs +++ b/url_serde/src/lib.rs @@ -51,9 +51,12 @@ ipc::channel::>() 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}; @@ -95,6 +98,18 @@ impl<'a> Serialize for Ser<'a, Url> { } +/// Serializes this Option into a `serde` stream. +impl<'a> Serialize for Ser<'a, Option> { + fn serialize(&self, serializer: S) -> Result 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 @@ -135,6 +150,21 @@ impl Deserialize for De { } +/// Deserializes this Option from a `serde` stream. +impl Deserialize for De> { + fn deserialize(deserializer: D) -> Result>, D::Error> where D: Deserializer { + let option_representation: Option = 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` or an `HashMap` need to be passed to serde. #[derive(Clone, Eq, Hash, PartialEq)] @@ -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 + } + + 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 + } + + 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); +}