Просмотр исходного кода

Add url_serde crate with ser/de support for Url type

Maximilian Hristache 9 лет назад
Родитель
Сommit
3feb0892e3
5 измененных файлов с 248 добавлено и 1 удалено
  1. 1 1
      Cargo.toml
  2. 1 0
      Makefile
  3. 22 0
      url_serde/Cargo.toml
  4. 9 0
      url_serde/README.md
  5. 215 0
      url_serde/src/lib.rs

+ 1 - 1
Cargo.toml

@@ -12,7 +12,7 @@ keywords = ["url", "parser"]
 license = "MIT/Apache-2.0"
 
 [workspace]
-members = [".", "idna"]
+members = [".", "idna", "url_serde"]
 
 [[test]]
 name = "unit"

+ 1 - 0
Makefile

@@ -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

+ 22 - 0
url_serde/Cargo.toml

@@ -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

+ 9 - 0
url_serde/README.md

@@ -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/).

+ 215 - 0
url_serde/src/lib.rs

@@ -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);
+}