diff --git a/.travis.yml b/.travis.yml index 3838a70..56b0e27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,12 @@ sudo: false matrix: include: - rust: stable + env: FEATURES="serde_impl" - rust: nightly - env: FEATURES="--features nightly" + env: FEATURES="serde_impl nightly" script: - - cargo build $FEATURES - - cargo test $FEATURES + - cargo build --features "$FEATURES" + - cargo test --features "$FEATURES" - cargo doc --no-deps after_success: | [ "$TRAVIS_RUST_VERSION" = nightly ] && diff --git a/Cargo.toml b/Cargo.toml index d528464..f10337b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,11 @@ readme = "README.md" [features] nightly = [] +serde_impl = ["serde", "serde_json"] + +[dependencies] +serde = { version = "^0.7", optional = true } +serde_json = { version = "^0.7", optional = true } [lib] test = false diff --git a/src/lib.rs b/src/lib.rs index 5699b6a..f62096c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,10 @@ #![deny(missing_docs)] +// Optional Serde support +#[cfg(feature = "serde_impl")] +pub mod serde; + use std::borrow::Borrow; use std::fmt::{self, Debug}; use std::iter; diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000..2d2a1e7 --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,84 @@ +//! An optional implementation of serialization/deserialization. Reference +//! implementations used: +//! +//! - [Serialize][1]. +//! - [Deserialize][2]. +//! +//! [1]: https://github.com/serde-rs/serde/blob/97856462467db2e90cf368e407c7ebcc726a01a9/serde/src/ser/impls.rs#L601-L611 +//! [2]: https://github.com/serde-rs/serde/blob/97856462467db2e90cf368e407c7ebcc726a01a9/serde/src/de/impls.rs#L694-L746 + +extern crate serde; + +use super::LinearMap; + +use self::serde::{Serialize, Serializer, Deserialize, Deserializer}; +use self::serde::ser::impls::MapIteratorVisitor; +use self::serde::de::{Visitor, MapVisitor, Error}; + +use std::marker::PhantomData; + +impl Serialize for LinearMap + where K: Serialize + Ord, + V: Serialize, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer, + { + serializer.serialize_map(MapIteratorVisitor::new(self.iter(), Some(self.len()))) + } +} + +#[allow(missing_docs)] +pub struct LinearMapVisitor { + marker: PhantomData>, +} + +impl LinearMapVisitor { + #[allow(missing_docs)] + pub fn new() -> Self { + LinearMapVisitor { + marker: PhantomData, + } + } +} + +impl Visitor for LinearMapVisitor + where K: Deserialize + Eq, + V: Deserialize, +{ + type Value = LinearMap; + + #[inline] + fn visit_unit(&mut self) -> Result + where E: Error, + { + Ok(LinearMap::new()) + } + + #[inline] + fn visit_map(&mut self, mut visitor: Visitor) -> Result + where Visitor: MapVisitor, + { + let mut values = LinearMap::with_capacity(visitor.size_hint().0); + + while let Some((key, value)) = try!(visitor.visit()) { + values.insert(key, value); + } + + try!(visitor.end()); + + Ok(values) + } +} + +impl Deserialize for LinearMap + where K: Deserialize + Eq, + V: Deserialize, +{ + fn deserialize(deserializer: &mut D) -> Result, D::Error> + where D: Deserializer, + { + deserializer.deserialize_map(LinearMapVisitor::new()) + } +} diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 0000000..d8088f1 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,44 @@ +#![cfg(feature = "serde_impl")] + +extern crate linear_map; +extern crate serde; +extern crate serde_json; + +use linear_map::LinearMap; + +#[test] +fn test_ser_empty() { + let map = LinearMap::::new(); + let j = serde_json::to_string(&map).unwrap(); + let expected = "{}"; + assert_eq!(j, expected); +} + +#[test] +fn test_ser() { + let mut map = LinearMap::new(); + map.insert("b", 20); + map.insert("a", 10); + map.insert("c", 30); + + let j = serde_json::to_string(&map).unwrap(); + let expected = r#"{"b":20,"a":10,"c":30}"#; + assert_eq!(j, expected); +} + +#[test] +fn test_de_empty() { + let j = "{}"; + let map: LinearMap = serde_json::from_str(j).unwrap(); + assert_eq!(map.len(), 0); +} + +#[test] +fn test_de() { + let j = r#"{"b":20,"a":10,"c":30}"#; + let map: LinearMap = serde_json::from_str(j).unwrap(); + let items: Vec<_> = map.iter().map(|(k, v)| (k.clone(), *v)).collect(); + assert_eq!(items, [("b".to_owned(), 20), + ("a".to_owned(), 10), + ("c".to_owned(), 30)]); +}