Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serde support #47

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ install:
## Build Script ##
build: false
test_script:
- cargo build --verbose
- cargo test --verbose
- cargo build --verbose --all-features --tests
- cargo test --verbose --features serde

notifications:
- provider: Email
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ before_script: |
(test $(grep "cargo-travis" /home/travis/.cargo/.crates.toml | sed -r "s/\"cargo-travis ([^\ ]+).+/\1/") = $(cargo search cargo-travis --limit 1 | sed -r "s/cargo-travis \(([^\)]+)\).+/\1/") || cargoo install cargo-travis --force)

script: |
cargo build --verbose &&
(test "$TRAVIS_RUST_VERSION" == "1.20.0" || cargo test --verbose)
cargo build --verbose --all-features --tests &&
(test "$TRAVIS_RUST_VERSION" == "1.20.0" || cargo test --verbose --features serde)

after_success: |
test "$TRAVIS_RUST_VERSION" != "stable" || cargo coveralls
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`. Disabled by default. Enabled with the `use_serde` feature.

## [v0.16.0] — 2017-12-21
This release contains significant changes in order to support underlying storage types that
Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@ maintenance = { status = "actively-developed" }

[dependencies]
num = "0.1"
serde = { version = "1.0", optional = true, default-features = false }
typenum = "1.9.0"

[dev-dependencies]
approx = "0.1.1"
quickcheck = "0.5.0"
serde_json = "1.0"
static_assertions = "0.2.5"

[features]
default = ["f32", "f64", "si", "std"]
# The `use_serde` feature exists so that, in the future, we can add other dependency features to
# it, like num/serde. However, num/serde is currently left out because it has not yet been updated
# to Serde 1.0.
# It is also necessary to call it something other than `serde` because of this cargo bug:
# https://github.com/rust-lang/cargo/issues/1286
use_serde = ["serde"]
usize = []
u8 = []
u16 = []
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,18 @@ 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]
uom = {
version = "0.16.0",
default-features = false,
features = [
"use_serde", # Serde support.
"usize", "u8", "u16", "u32", "u64", # Unsigned integer storage types.
"isize", "i8", "i16", "i32", "i64", # Signed interger storage types.
"bigint", "biguint", # Arbitrary width integer storage types.
Expand All @@ -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.
* `use_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,
Expand Down
19 changes: 15 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,19 @@
//!
//! ## 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]
//! uom = {
//! version = "0.16.0",
//! default-features = false,
//! features = [
//! "use_serde", # Serde support.
//! "usize", "u8", "u16", "u32", "u64", # Unsigned integer storage types.
//! "isize", "i8", "i16", "i32", "i64", # Signed interger storage types.
//! "bigint", "biguint", # Arbitrary width integer storage types.
Expand All @@ -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.
//! * `use_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,
Expand Down Expand Up @@ -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)]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like cfg_attr isn't necessary. The compiler is smart enough that #[doc(hidden)] doesn't "fall through" to the next item when the serde feature is not enabled.

#[cfg(feature = "serde")]
pub extern crate serde;

#[doc(hidden)]
pub extern crate typenum;

Expand All @@ -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;
Expand Down
36 changes: 36 additions & 0 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,42 @@ macro_rules! system {
}
}

#[cfg(feature = "serde")]
impl<D, U, V> $crate::serde::Serialize for Quantity<D, U, V>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V> + $crate::serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: $crate::serde::Serializer
{
self.value.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de, D, U, V> $crate::serde::Deserialize<'de> for Quantity<D, U, V>
where
D: Dimension + ?Sized,
U: Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V> + $crate::serde::Deserialize<'de>,
{
fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>
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.
///
Expand Down
28 changes: 26 additions & 2 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use num::{Float, FromPrimitive, One, Saturating, Signed, Zero};
use quickcheck::TestResult;
use lib::fmt::Debug;
use lib::marker::PhantomData;
#[cfg(feature = "serde")]
use serde_json;
#[allow(unused_imports)]
use typenum::{N1, P1, P2, P3, Z0};

Expand Down Expand Up @@ -527,7 +529,7 @@ mod system_macro {
&(Length::new::<meter>((*l).clone())
% Length::new::<meter>((*r).clone())).get(meter)))
}
}
}
}

mod prim_int {
Expand Down Expand Up @@ -710,7 +712,7 @@ mod system_macro {
}
}

mod op_assign {
mod primitive {
storage_types! {
types: Float, PrimInt;

Expand Down Expand Up @@ -781,6 +783,28 @@ mod system_macro {

TestResult::from_bool(Test::approx_eq(&f, &v.get(meter)))
}

// These serde tests can't be run against num-backed numeric backends because the num
// crate hasn't been updated to Serde 1.0 yet.
#[cfg(feature = "serde")]
#[allow(trivial_casts)]
fn serde_serialize(v: A<V>) -> bool {
let m = Length::new::<meter>((*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<V>) -> 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))
}
}
}
}
Expand Down