From faca99ee8984737d039b358fb0065af808dcb17d 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. --- .appveyor.yml | 1 + .travis.yml | 1 + CHANGELOG.md | 2 ++ Cargo.toml | 2 ++ README.md | 12 ++++++++---- src/lib.rs | 19 +++++++++++++++---- src/system.rs | 36 ++++++++++++++++++++++++++++++++++++ src/tests.rs | 22 ++++++++++++++++++++++ 8 files changed, 87 insertions(+), 8 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3c815bad..c706e752 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -56,6 +56,7 @@ build: false test_script: - cargo build --verbose - cargo test --verbose + - cargo test --verbose --features serde notifications: - provider: Email 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..dd47d13b 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`. Only available when the `serde` feature is enabled. ## [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..eefd9d09 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] @@ -79,6 +79,7 @@ uom = { "bigint", "biguint", # Arbitrary width integer storage types. "rational", "rational32", "rational64", "bigrational", # Integer ratio storage types. "f32", "f64", # Floating point storage types. + "serde", # Serde support. "si", "std", # Built-in SI system and std library support. ] } @@ -88,12 +89,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. Disabled by default. * `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..2f397977 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,10 +52,11 @@ //! //! ## 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] @@ -68,6 +69,7 @@ //! "bigint", "biguint", # Arbitrary width integer storage types. //! "rational", "rational32", "rational64", "bigrational", # Integer ratio storage types. //! "f32", "f64", # Floating point storage types. +//! "serde", # Serde support. //! "si", "std", # Built-in SI system and std library support. //! ] //! } @@ -77,12 +79,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. Disabled by default. //! * `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 +161,10 @@ 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; + #[doc(hidden)] pub extern crate typenum; @@ -165,6 +174,8 @@ extern crate approx; #[cfg(test)] #[macro_use] extern crate quickcheck; +#[cfg(all(test, feature = "serde"))] +extern crate serde_json; #[cfg(test)] #[macro_use] extern crate static_assertions; 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..3eac6db4 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"))] +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_f = serde_json::to_string(&*v).expect("Must be able to serialize num"); + let json_q = serde_json::to_string(&m).expect("Must be able to serialize Quantity"); + + 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 serialize num"); + let length: Length = serde_json::from_str(&json_f) + .expect("Must be able to deserialize Quantity"); + + Test::approx_eq(&*v, &length.get(meter)) + } } }