Skip to content

Commit

Permalink
Add serde (de)serialization (#9)
Browse files Browse the repository at this point in the history
Fix #8
  • Loading branch information
tdelmas authored Nov 5, 2023
1 parent 143763e commit 04c970a
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 15 deletions.
8 changes: 2 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ jobs:
- checkout
- run: rustup toolchain install nightly
- run: cargo +nightly -Zscript ./pre-publish.rs
- run:
command: cargo test --no-run
no_output_timeout: 15m
- run: cargo test
- run: cargo test --test 'serde' --features serde
build_and_test_arm:
machine:
image: ubuntu-2004:current
Expand All @@ -20,10 +18,8 @@ jobs:
- run: curl https://sh.rustup.rs -sSf | sh -s -- -y
- run: rustup toolchain install nightly
- run: cargo +nightly -Zscript ./pre-publish.rs
- run:
command: cargo test --no-run
no_output_timeout: 15m
- run: cargo test
- run: cargo test --test 'serde' --features serde

workflows:
version: 2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ jobs:
cargo +nightly -Zscript ./pre-publish.rs
rustup toolchain install ${{ matrix.version }}
cargo build
- name: Build tests
run: cargo test --no-run
- name: Run tests
run: cargo test --verbose
- name: Run serde tests
run: cargo test --verbose --test 'serde' --features serde

clippy:
runs-on: ubuntu-latest
Expand Down
45 changes: 45 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ The only method that can `panic!` is the `unsafe` method `new_unchecked` when us

A `panic!` triggered in any other way is considered a security bug and should be reported.

## Overhead
## Minimal overhead

This crate is designed to have a minimal overhead at runtime, in terms of memory, speed and binary size.

Expand All @@ -109,6 +109,10 @@ In debug mode, a little overhead is present, both to check the validity of the v

Any other overhead is considered a bug and should be reported.

# Features

- `serde`: implements `Serialize` and `Deserialize` for all types

## How it works

For each operation, at compile time crate determine the most strict type possible for the result.
Expand All @@ -121,7 +125,6 @@ Methods that takes another float as parameter will also return the most strict t
## Main limitations

- Doesn't work with `no_std` (for now)
- Doesn't implement `serde` serialization/deserialization (yet)
- Doesn't fix the floating point quirks such as `0.0 == -0.0` (because that would introduce a runtime overhead)

## Testing
Expand Down
9 changes: 4 additions & 5 deletions typed_floats/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ rust-version = "1.56"

[features]
default = []

#[lib]
#name = "typed_floats"
#path = "src/lib.rs"
serde = ["dep:serde"]

[dependencies]
thiserror = "1.0"
typed_floats_macros = {version = "=0.3.0", path = "../typed_floats_macros"}
serde = { version = "1.0", features = ["derive"], optional = true }

[dev-dependencies]
# For examples
checked-float = "0.1.4"
ordered-float = "4.1.0"
ordered-float = "4.1.0"
serde_json = { version = "1.0" }
24 changes: 24 additions & 0 deletions typed_floats/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,9 @@ pub enum FromStrError {
InvalidNumber(InvalidNumber),
}

#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize};

use core::num::{NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8};
use core::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};

Expand Down Expand Up @@ -777,4 +780,25 @@ pub mod tf32 {
add_const!(LN_2, f32, PositiveFinite, "ln(2)");
add_const!(LN_10, f32, PositiveFinite, "ln(10)");
}

#[cfg(feature = "serde")]
#[test]
fn test_serde() {
let map = serde_json::json!({
"a": 1.0,
});

#[derive(Serialize)]
struct A {
a: NonNaN,
}

let a = A {
a: NonNaN::try_from(1.0).unwrap(),
};

let a_json = serde_json::to_value(a).unwrap();

assert_eq!(a_json, map);
}
}
26 changes: 26 additions & 0 deletions typed_floats/tests/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![cfg(feature = "serde")]

use typed_floats::*;

#[test]
fn test_serde_serialize() {
let a: Positive<f64> = 3.0f64.try_into().unwrap();

let serialized = serde_json::to_string(&a).unwrap();

assert_eq!(serialized, "3.0");
}

#[test]
fn test_serde_deserialize() {
let json = "3.0";
let a: Positive<f64> = serde_json::from_str(json).unwrap();

assert_eq!(a, 3.0f64);

let json = "-3.0";
let a: Result<Positive<f64>, _> = serde_json::from_str(json);

assert!(a.is_err());
assert_eq!(a.unwrap_err().to_string(), "Number is negative");
}
13 changes: 13 additions & 0 deletions typed_floats_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ fn do_generate_generic_floats(
///
/// It satisfies the following constraints:
#constraints
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct #name<T=#default_float_type>(T);
Expand All @@ -284,6 +285,18 @@ fn do_generate_floats(floats: &[FloatDefinition]) -> proc_macro2::TokenStream {
.get_compiler_hints(&syn::Ident::new("value", proc_macro2::Span::call_site()));

output.extend(quote! {
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for #full_type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let val: #float_type = Deserialize::deserialize(deserializer)?;

val.try_into().map_err(serde::de::Error::custom)
}
}

impl #full_type {
/// Creates a new value from a primitive type
/// It adds a little overhead compared to `new_unchecked`
Expand Down

0 comments on commit 04c970a

Please sign in to comment.