blob: ebb22a8d83c5d27c16a810e751226d5bad6b37dd [file] [log] [blame]
// Copyright 2017 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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(with = "url_serde")]
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;
#[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<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())
}
}
/// 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()
}
}
}
impl<'a, String> Serialize for Ser<'a, Host<String>> where String: AsRef<str> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 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<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where D: Deserializer<'de>, De<T>: Deserialize<'de>
{
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<'de, T> De<T> where De<T>: serde::Deserialize<'de> {
/// 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<'de> Deserialize<'de> for De<Url> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
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<URL> from a `serde` stream.
impl<'de> Deserialize<'de> for De<Option<Url>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
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))
}
}
impl<'de> Deserialize<'de> for De<Host> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
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<T>` or an `HashMap<K, V>` need to be passed to serde.
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Serde<T>(pub T);
/// A convenience type alias for Serde<Url>.
pub type SerdeUrl = Serde<Url>;
impl<'de, T> Serde<T>
where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
{
/// Consumes this wrapper, returning the inner value.
#[inline(always)]
pub fn into_inner(self) -> T {
self.0
}
}
impl<'de, T> fmt::Debug for Serde<T>
where T: fmt::Debug, De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
{
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.0.fmt(formatter)
}
}
impl<'de, T> Deref for Serde<T>
where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
{
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<'de, T> DerefMut for Serde<T>
where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
{
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<'de, T: PartialEq> PartialEq<T> for Serde<T>
where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
{
fn eq(&self, other: &T) -> bool {
self.0 == *other
}
}
impl<'de, T> Deserialize<'de> for Serde<T>
where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
De::deserialize(deserializer).map(De::into_inner).map(Serde)
}
}
impl<'de, T> Serialize for Serde<T>
where De<T>: Deserialize<'de>, 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() {
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<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() {
#[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<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);
}
#[test]
fn test_derive_with_for_url() {
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(with = "self", rename = "_url_")]
url: Url
}
let url_str = "http://www.test.com/foo/bar?$param=bazz";
let json_string = format!(r#"{{"_url_":"{}"}}"#, url_str);
// test deserialization
let expected = Test {
url: Url::parse(url_str).unwrap()
};
let got: Test = serde_json::from_str(&json_string).unwrap();
assert_eq!(expected, got);
// test serialization
let input = Test {url: Url::parse(url_str).unwrap()};
let got = serde_json::to_string(&input).unwrap();
assert_eq!(json_string, 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<Host> = serde_json::from_str(&json).unwrap();
assert_eq!(de.into_inner(), *host)
}
}