From ee6f5099fe393918343595d35822e95c65755f53 Mon Sep 17 00:00:00 2001 From: "radix@twistedmatrix.com" Date: Wed, 27 Dec 2017 16:36:17 -0600 Subject: [PATCH] Implement `serde::Serialize` and `serde::Deserialize`. Fixes #37. --- .travis.yml | 1 + CHANGELOG.md | 2 ++ Cargo.toml | 2 ++ README.md | 11 +++++++---- src/lib.rs | 18 ++++++++++++++---- src/system.rs | 36 ++++++++++++++++++++++++++++++++++++ src/tests.rs | 22 ++++++++++++++++++++++ 7 files changed, 84 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9823148d..85479794 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ before_script: | script: | cargo build --verbose && (test "$TRAVIS_RUST_VERSION" == "1.20.0" || cargo test --verbose) + (test "$TRAVIS_RUST_VERSION" == "1.20.0" || cargo test --verbose --features serde) after_success: | test "$TRAVIS_RUST_VERSION" != "stable" || cargo coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b51264a..d3255d97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ ### Added * [#26](https://github.com/iliekturtles/uom/issues/26) Implement `num::Zero`. * [#35](https://github.com/iliekturtles/uom/issues/35) Implement `num::Saturating`. + * [#37](https://github.com/iliekturtles/uom/issues/35) Implement `serde::Serialize` and + `serde::Deserialize`. ## [v0.16.0] — 2017-12-21 This release contains significant changes in order to support underlying storage types that diff --git a/Cargo.toml b/Cargo.toml index dec1457f..e8f52b4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,13 @@ maintenance = { status = "actively-developed" } [dependencies] num = "0.1" +serde = { version = "1.0", optional = true } typenum = "1.9.0" [dev-dependencies] approx = "0.1.1" quickcheck = "0.5.0" +serde_json = "1.0" static_assertions = "0.2.5" [features] diff --git a/README.md b/README.md index 36997595..cb7a6ce5 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,10 @@ See the [examples](examples) directory for more advanced usage: ## Features `uom` has multiple `Cargo` features for controlling available underlying storage types, the -inclusion of the pre-built [International System of Units][si] (SI), and `no_std` functionality. The -features are described below. `f32`, `f64`, `std`, and `si` are enabled by default. Features can be -cherry-picked by using the `--no-default-features` and `--features "..."` flags when compiling `uom` -or specifying features in Cargo.toml: +inclusion of the pre-built [International System of Units][si] (SI), support for [Serde][serde], and +`no_std` functionality. The features are described below. `f32`, `f64`, `std`, and `si` are enabled +by default. Features can be cherry-picked by using the `--no-default-features` and +`--features "..."` flags when compiling `uom` or specifying features in Cargo.toml: ```toml [dependencies] @@ -88,12 +88,15 @@ uom = { `rational`, `rational32`, `rational64`, `bigrational`, `f32`, `f64` -- Features to enable underlying storage types. At least one of these features must be enabled. `f32` and `f64` are enabled by default. + * `serde` -- Feature to enable support for serialization and deserialization of quantities with + the [serde][serde] crate. * `si` -- Feature to include the pre-built [International System of Units][si] (SI). Enabled by default. * `std` -- Feature to compile with standard library support. Disabling this feature compiles `uom` with `no_std`. Enabled by default. [si]: http://jcgm.bipm.org/vim/en/1.16.html +[serde]: https://serde.rs/ ## Design Rather than working with [measurement units](http://jcgm.bipm.org/vim/en/1.9.html) (meter, diff --git a/src/lib.rs b/src/lib.rs index c1bd2869..ec3354c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,10 +52,10 @@ //! //! ## Features //! `uom` has multiple `Cargo` features for controlling available underlying storage types, the -//! inclusion of the pre-built [International System of Units][si] (SI), and `no_std` functionality. -//! The features are described below. `f32`, `f64`, `std`, and `si` are enabled by default. Features -//! can be cherry-picked by using the `--no-default-features` and `--features "..."` flags when -//! compiling `uom` or specifying features in Cargo.toml: +//! inclusion of the pre-built [International System of Units][si] (SI), support for [Serde][serde], +//! and `no_std` functionality. The features are described below. `f32`, `f64`, `std`, and `si` are +//! enabled by default. Features can be cherry-picked by using the `--no-default-features` and +//! `--features "..."` flags when compiling `uom` or specifying features in Cargo.toml: //! //! ```toml //! [dependencies] @@ -77,12 +77,15 @@ //! `rational`, `rational32`, `rational64`, `bigrational`, `f32`, `f64` -- Features to enable //! underlying storage types. At least one of these features must be enabled. `f32` and `f64` are //! enabled by default. +//! * `serde` -- Feature to enable support for serialization and deserialization of quantities with +//! the [serde][serde] crate. //! * `si` -- Feature to include the pre-built [International System of Units][si] (SI). Enabled by //! default. //! * `std` -- Feature to compile with standard library support. Disabling this feature compiles //! `uom` with `no_std`. Enabled by default. //! //! [si]: http://jcgm.bipm.org/vim/en/1.16.html +//! [serde]: https://serde.rs/ //! //! ## Design //! Rather than working with [measurement units](http://jcgm.bipm.org/vim/en/1.9.html) (meter, @@ -156,6 +159,13 @@ compile_error!("A least one underlying storage type must be enabled. See the fea #[doc(hidden)] pub extern crate num; +#[doc(hidden)] +#[cfg(feature = "serde")] +pub extern crate serde; + +#[cfg(all(feature = "serde", test))] +extern crate serde_json; + #[doc(hidden)] pub extern crate typenum; diff --git a/src/system.rs b/src/system.rs index 516a2d99..9fc8fe5d 100644 --- a/src/system.rs +++ b/src/system.rs @@ -873,6 +873,42 @@ macro_rules! system { } } + #[cfg(feature = "serde")] + impl $crate::serde::Serialize for Quantity + where + D: Dimension + ?Sized, + U: Units + ?Sized, + V: $crate::num::Num + $crate::Conversion + $crate::serde::Serialize, + { + fn serialize(&self, serializer: S) -> Result + where + S: $crate::serde::Serializer + { + self.value.serialize(serializer) + } + } + + #[cfg(feature = "serde")] + impl<'de, D, U, V> $crate::serde::Deserialize<'de> for Quantity + where + D: Dimension + ?Sized, + U: Units + ?Sized, + V: $crate::num::Num + $crate::Conversion + $crate::serde::Deserialize<'de>, + { + fn deserialize(deserializer: De) -> Result + where + De: $crate::serde::Deserializer<'de>, + { + let value: V = $crate::serde::Deserialize::deserialize(deserializer)?; + + Ok(Quantity { + dimension: $crate::lib::marker::PhantomData, + units: $crate::lib::marker::PhantomData, + value, + }) + } + } + /// Macro to implement [`quantity`](si/struct.Quantity.html) type aliases for a specific /// [system of units][units] and value storage type. /// diff --git a/src/tests.rs b/src/tests.rs index 6501e4a3..70fc6860 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -7,6 +7,8 @@ use num::{Float, FromPrimitive, One, Saturating, Signed, Zero}; use quickcheck::TestResult; use lib::fmt::Debug; use lib::marker::PhantomData; +#[cfg(all(feature = "serde", test))] +use serde_json; #[allow(unused_imports)] use typenum::{N1, P1, P2, P3, Z0}; @@ -527,6 +529,26 @@ mod system_macro { &(Length::new::((*l).clone()) % Length::new::((*r).clone())).get(meter))) } + + #[cfg(feature = "serde")] + #[allow(trivial_casts)] + fn serde_serialize(v: A) -> bool { + let m = Length::new::((*v).clone()); + let json_q = serde_json::to_string(&m).expect("Must be able to serialize Quantity"); + let json_f = serde_json::to_string(&*v).expect("Must be able to serialize num"); + + json_f == json_q + } + + #[cfg(feature = "serde")] + #[allow(trivial_casts)] + fn serde_deserialize(v: A) -> bool { + let json_f = serde_json::to_string(&*v).expect("Must be able to deserialize num"); + let length: Length = serde_json::from_str(&json_f) + .expect("Must be able to deserialize Quantity"); + + Test::approx_eq(&length.get(meter), &*v) + } } }