// Copyright 2017 The rust-url developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /*! 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::>() ``` */ #![deny(missing_docs)] #![deny(unsafe_code)] extern crate serde; #[cfg(test)] #[macro_use] extern crate serde_derive; #[cfg(test)] extern crate serde_json; extern crate url; use serde::{Deserialize, Serialize, Serializer, Deserializer}; use std::cmp::PartialEq; use std::error::Error; use std::fmt; use std::io::Write; use std::ops::{Deref, DerefMut}; use std::str; use url::{Url, Host}; /// 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(value: &T, serializer: S) -> Result 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(&self, serializer: S) -> Result where S: Serializer { serializer.serialize_str(self.0.as_str()) } } /// 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() } } } impl<'a, String> Serialize for Ser<'a, Host> where String: AsRef { fn serialize(&self, serializer: S) -> Result where S: Serializer { match *self.0 { Host::Domain(ref s) => serializer.serialize_str(s.as_ref()), Host::Ipv4(_) | Host::Ipv6(_) => { // max("101.102.103.104".len(), // "[1000:1002:1003:1004:1005:1006:101.102.103.104]".len()) const MAX_LEN: usize = 47; let mut buffer = [0; MAX_LEN]; serializer.serialize_str(display_into_buffer(&self.0, &mut buffer)) } } } } /// Like .to_string(), but doesn’t allocate memory for a `String`. /// /// Panics if `buffer` is too small. fn display_into_buffer<'a, T: fmt::Display>(value: &T, buffer: &'a mut [u8]) -> &'a str { let remaining_len; { let mut remaining = &mut *buffer; write!(remaining, "{}", value).unwrap(); remaining_len = remaining.len() } let written_len = buffer.len() - remaining_len; let written = &buffer[..written_len]; // write! only provides std::fmt::Formatter to Display implementations, // which has methods write_str and write_char but no method to write arbitrary bytes. // Therefore, `written` is well-formed in UTF-8. #[allow(unsafe_code)] unsafe { str::from_utf8_unchecked(written) } } /// 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(deserializer: D) -> Result where D: Deserializer, De: 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); impl De where De: 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 { fn deserialize(deserializer: D) -> Result 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()) }) } } /// Deserializes this Option from a `serde` stream. impl Deserialize for De> { fn deserialize(deserializer: D) -> Result 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)) } } impl Deserialize for De { fn deserialize(deserializer: D) -> Result where D: Deserializer { let string_representation: String = Deserialize::deserialize(deserializer)?; Host::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` or an `HashMap` need to be passed to serde. #[derive(Clone, Eq, Hash, PartialEq)] pub struct Serde(pub T) where De: Deserialize, for<'a> Ser<'a, T>: Serialize; /// A convenience type alias for Serde. pub type SerdeUrl = Serde; impl Serde where De: 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 fmt::Debug for Serde where T: fmt::Debug, De: Deserialize, for<'a> Ser<'a, T>: Serialize { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.0.fmt(formatter) } } impl Deref for Serde where De: Deserialize, for<'a> Ser<'a, T>: Serialize { type Target = T; fn deref(&self) -> &T { &self.0 } } impl DerefMut for Serde where De: Deserialize, for<'a> Ser<'a, T>: Serialize { fn deref_mut(&mut self) -> &mut T { &mut self.0 } } impl PartialEq for Serde where De: Deserialize, for<'a> Ser<'a, T>: Serialize { fn eq(&self, other: &T) -> bool { self.0 == *other } } impl Deserialize for Serde where De: Deserialize, for<'a> Ser<'a, T>: Serialize { fn deserialize(deserializer: D) -> Result where D: Deserializer { De::deserialize(deserializer).map(De::into_inner).map(Serde) } } impl Serialize for Serde where De: Deserialize, for<'a> Ser<'a, T>: Serialize { fn serialize(&self, serializer: S) -> Result where S: Serializer { Ser(&self.0).serialize(serializer) } } #[test] fn test_ser_de_url() { 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); } #[test] fn test_derive_deserialize_with_for_url() { #[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() { #[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() { #[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() { #[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); } #[test] fn test_host() { for host in &[ Host::Domain("foo.com".to_owned()), Host::Ipv4("127.0.0.1".parse().unwrap()), Host::Ipv6("::1".parse().unwrap()), ] { let json = serde_json::to_string(&Ser(host)).unwrap(); let de: De = serde_json::from_str(&json).unwrap(); assert_eq!(de.into_inner(), *host) } }