Skip to content

Commit

Permalink
Merge pull request #33 from greyblake/float-ord
Browse files Browse the repository at this point in the history
Float ord
  • Loading branch information
greyblake committed Apr 22, 2023
2 parents 9ec590a + 1620e15 commit cd8f0b5
Show file tree
Hide file tree
Showing 24 changed files with 426 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### v0.3.0 - 2023-??-??
* Add `finite` validation for float types which checks against NaN and infinity.
* Enable deriving of `Eq` and `Ord` on float types (if `finite` validation is present)

### v0.2.0 - 2023-04-13

Expand Down
17 changes: 17 additions & 0 deletions Cargo.lock

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

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ The float inner types are: `f32`, `f64`.
The following traits can be derived for a float-based type:
`Debug`, `Clone`, `Copy`, `PartialEq`, `PartialOrd`, `FromStr`, `AsRef`, `Into`, `From`, `TryFrom`, `Hash`, `Borrow`, `Display`, `Serialize`, `Deserialize`.

It's also possible to derive `Eq` and `Ord` if the validation rules guarantee that `NaN` is excluded.
This can be done applying by `finite` validation. For example:

```rust
#[nutype(validate(finite))]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Size(f64);
```

## Custom sanitizers

You can set custom sanitizers using the `with` option.
Expand Down
22 changes: 19 additions & 3 deletions WORKLOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
## Roadmap
* JsonSchema: add README, lib docs
* Use `enum-kind` crate
## TODO
* Update to Rust 1.69

## Tech debt
* Make individual UI tests possible to opt-out depending on the feature flags.
* Use `enum-kind` crate

### TODO Refactor:
* Refactor parsers
Expand All @@ -19,6 +19,11 @@
* Ord requires PartialOrd
* Eq requires PartialEq

## Ideas for recipes (docs)
* Derive Eq and Ord on float based type if `finite` validation is set
* Validating strings with regex


### Later
* Support decimals libraries:
* https://crates.io/crates/rust_decimal
Expand Down Expand Up @@ -120,3 +125,14 @@
* JsonSchema: Add unit tests
* JsonSchema: UI test
* Validation with regex
* JsonSchema: add README, lib docs
* Implement unit tests for float Ord
* Test cmp
* Test sort()
* Implement UI tests for derive(Ord), cover cases:
* When finite is not set
* When Eq is not derived
* When PartialOrd is not derived
* Prop-based tests for Ord
* Add note about Eq and Ord on floats in README
* Update CHANGELOG
19 changes: 17 additions & 2 deletions dummy/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use nutype::nutype;
use schemars::JsonSchema;
#[nutype(validate(max = 12.34))]
#[derive(FromStr, Display, Clone, Copy, Serialize, Deserialize, JsonSchema)]
#[nutype(validate(finite, max = 12.34))]
#[derive(FromStr, Display, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct Dist(f64);

#[nutype(
Expand All @@ -27,6 +27,13 @@ lazy_static! {
#[derive(Debug)]
pub struct PinCode(String);

#[nutype(
new_unchecked
validate(finite)
)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct Coefficient(f64);

fn main() {
let dist: Dist = "11.4".parse().unwrap();
println!("dist = {}", dist.into_inner());
Expand All @@ -49,4 +56,12 @@ fn main() {

let pin_result = PinCode::new("1223 ");
println!("\npin_result = {pin_result:?}\n");

let k1 = Coefficient::new(0.0).unwrap();
let k2 = Coefficient::new(1.21).unwrap();
let k3 = Coefficient::new(3.21).unwrap();

let mut ks = [k3, k1, k2, k1, k3];
ks.sort();
println!("{ks:?}");
}
11 changes: 11 additions & 0 deletions nutype/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,17 @@
//! The following traits can be derived for a float-based type:
//! `Debug`, `Clone`, `Copy`, `PartialEq`, `PartialOrd`, `FromStr`, `AsRef`, `Into`, `From`, `TryFrom`, `Hash`, `Borrow`, `Display`, `Serialize`, `Deserialize`.
//!
//! It's also possible to derive `Eq` and `Ord` if the validation rules guarantee that `NaN` is excluded.
//! This can be done by applying `finite` validation. For example:
//!
//! ```rust
//! use nutype::nutype;
//!
//! #[nutype(validate(finite))]
//! #[derive(PartialEq, Eq, PartialOrd, Ord)]
//! struct Size(f64);
//! ```
//!
//! ## Custom sanitizers
//!
//! You can set custom sanitizers using the `with` option.
Expand Down
2 changes: 1 addition & 1 deletion nutype_macros/src/common/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ pub enum DeriveTrait {
Normal(NormalDeriveTrait),
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NormalDeriveTrait {
// Standard library
Debug,
Expand Down
33 changes: 33 additions & 0 deletions nutype_macros/src/float/gen/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ enum FloatIrregularTrait {
AsRef,
Into,
From,
Eq,
Ord,
TryFrom,
Borrow,
Display,
Expand All @@ -57,9 +59,11 @@ impl From<FloatDeriveTrait> for FloatGeneratableTrait {
FloatDeriveTrait::PartialEq => {
FloatGeneratableTrait::Transparent(FloatTransparentTrait::PartialEq)
}
FloatDeriveTrait::Eq => FloatGeneratableTrait::Irregular(FloatIrregularTrait::Eq),
FloatDeriveTrait::PartialOrd => {
FloatGeneratableTrait::Transparent(FloatTransparentTrait::PartialOrd)
}
FloatDeriveTrait::Ord => FloatGeneratableTrait::Irregular(FloatIrregularTrait::Ord),
FloatDeriveTrait::FromStr => {
FloatGeneratableTrait::Irregular(FloatIrregularTrait::FromStr)
}
Expand Down Expand Up @@ -161,6 +165,35 @@ fn gen_implemented_traits(
inner_type,
maybe_error_type_name.as_ref(),
),
FloatIrregularTrait::Eq => gen_impl_trait_eq(type_name),
FloatIrregularTrait::Ord => gen_impl_trait_ord(type_name),
})
.collect()
}

fn gen_impl_trait_eq(type_name: &TypeName) -> TokenStream {
quote! {
impl ::core::cmp::Eq for #type_name { }
}
}

// The implementation below may panic.
// Function `partial_cmp` returns `None` only for `NaN` values, but
// `NaN` values are supposed to be exluded by `finite` validation rule.
// Without `finite` validation deriving `Ord` is not allowed.
fn gen_impl_trait_ord(type_name: &TypeName) -> TokenStream {
let tp = type_name.to_string();
quote! {
// Make clippy ignore this manual implementation of Ord even when PartialOrd is derived.
#[allow(clippy::derive_ord_xor_partial_ord)]
impl ::core::cmp::Ord for #type_name {
fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
self.partial_cmp(other)
.unwrap_or_else(|| {
let tp = #tp;
panic!("{tp}::cmp() panicked, because partial_cmp() returned None. Could it be that you're using unsafe {tp}::new_unchecked() ?", tp=tp);
})
}
}
}
}
2 changes: 2 additions & 0 deletions nutype_macros/src/float/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ pub enum FloatDeriveTrait {
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
FromStr,
AsRef,
Into,
Expand Down
Loading

0 comments on commit cd8f0b5

Please sign in to comment.