Skip to content

Commit 23ab4eb

Browse files
committed
Replace float_deserialize feature with deserialize_via_float method
1 parent 8d4a21d commit 23ab4eb

File tree

7 files changed

+114
-82
lines changed

7 files changed

+114
-82
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ optional = true
1515
default-features = false
1616

1717
[features]
18-
float_deserialize = ["serde"]
1918
default = ["std"]
2019
"rust_1.81" = []
2120
std = []

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ implementations of `std::error::Error` for `ParseIntError` and
3434
without the `std` feature.
3535

3636
Deserialization can be routed through `f64` instead of `u64` with the
37-
`float_deserialize` feature. This will still not deserialize numbers with a
37+
`(U)Int::deserialize_via_float` methods. This will still not deserialize numbers with a
3838
non-zero fractional component. Please be aware that
3939
`serde_json` doesn't losslessly parse large floats with a fractional part by
4040
default (even if the fractional part is `.0`). To fix that, enable its

src/int.rs

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,42 @@ impl Int {
433433
Self::new_saturating(self.0.saturating_pow(exp))
434434
}
435435

436+
/// Deserialization function for use with `#[serde(deserialize_with = ...)]` that performs
437+
/// deserialization through `f64` instead of `i64`.
438+
/// This allows deserializing from numbers with a fractional component like `.0`.
439+
///
440+
/// # Example
441+
/// ```rs
442+
/// use serde::Deserialize;
443+
/// use js_int::Int;
444+
///
445+
/// #[derive(Deserialize)]
446+
/// struct Point {
447+
/// #[serde(deserialize_with = "Int::deserialize_via_float")]
448+
/// x: Int;
449+
/// #[serde(deserialize_with = "Int::deserialize_via_float")]
450+
/// y: Int;
451+
/// }
452+
#[cfg(feature = "serde")]
453+
pub fn deserialize_via_float<'de, D>(deserializer: D) -> Result<Self, D::Error>
454+
where
455+
D: Deserializer<'de>,
456+
{
457+
const EXPECTING: &str =
458+
"a number between -2^53 + 1 and 2^53 - 1 without fractional component";
459+
460+
let val = f64::deserialize(deserializer)?;
461+
462+
if val > MAX_SAFE_INT as f64
463+
|| val < MIN_SAFE_INT as f64
464+
|| !super::is_acceptable_float(val)
465+
{
466+
Err(D::Error::invalid_value(Unexpected::Float(val), &EXPECTING))
467+
} else {
468+
Ok(Self(val as i64))
469+
}
470+
}
471+
436472
// TODO: wrapping_* methods, overflowing_* methods
437473
}
438474

@@ -576,34 +612,14 @@ impl<'de> Deserialize<'de> for Int {
576612
where
577613
D: Deserializer<'de>,
578614
{
579-
#[cfg(not(feature = "float_deserialize"))]
580-
{
581-
let val = i64::deserialize(deserializer)?;
582-
583-
Self::new(val).ok_or_else(|| {
584-
D::Error::invalid_value(
585-
Unexpected::Signed(val),
586-
&"an integer between -2^53 + 1 and 2^53 - 1",
587-
)
588-
})
589-
}
590-
591-
#[cfg(feature = "float_deserialize")]
592-
{
593-
const EXPECTING: &str =
594-
"a number between -2^53 + 1 and 2^53 - 1 without fractional component";
615+
let val = i64::deserialize(deserializer)?;
595616

596-
let val = f64::deserialize(deserializer)?;
597-
598-
if val > MAX_SAFE_INT as f64
599-
|| val < MIN_SAFE_INT as f64
600-
|| !super::is_acceptable_float(val)
601-
{
602-
Err(D::Error::invalid_value(Unexpected::Float(val), &EXPECTING))
603-
} else {
604-
Ok(Self(val as i64))
605-
}
606-
}
617+
Self::new(val).ok_or_else(|| {
618+
D::Error::invalid_value(
619+
Unexpected::Signed(val),
620+
&"an integer between -2^53 + 1 and 2^53 - 1",
621+
)
622+
})
607623
}
608624
}
609625

src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@
2828
//! * `serde`: Serialization and deserialization support via [serde](https://serde.rs). Disabled by
2929
//! default. You can use `js_int` + `serde` in `#![no_std]` contexts if you use
3030
//! `default-features = false` for both.
31-
//! * `float_deserialize`: Deserialize via `f64`, not via `u64`. If the input has a fraction,
32-
//! deserialization will fail.
33-
//! * `lax_deserialize`: Like `float_deserialize`, but if the input has a fraction, it is
3431
//! deserialized with the fractional part discarded.
3532
//! Please be aware that `serde_json` doesn't losslessly parse large floats with a fractional part
3633
//! by default (even if the fractional part is `.0`). To fix that, enable its `float_roundtrip`
@@ -59,7 +56,6 @@ pub use self::{
5956
uint::{UInt, MAX_SAFE_UINT},
6057
};
6158

62-
#[cfg(feature = "float_deserialize")]
6359
#[inline(always)]
6460
pub(crate) fn is_acceptable_float(float: f64) -> bool {
6561
!float.is_nan() && float.fract() == 0.0

src/uint.rs

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ use core::{
55
str::FromStr,
66
};
77

8-
use crate::{
9-
error::{ParseIntError, ParseIntErrorKind, TryFromIntError},
10-
MAX_SAFE_INT,
11-
};
8+
use crate::{error::{ParseIntError, ParseIntErrorKind, TryFromIntError}, MAX_SAFE_INT};
129
#[cfg(feature = "serde")]
1310
use serde::{
1411
de::{Error as _, Unexpected},
@@ -414,6 +411,39 @@ impl UInt {
414411
Self::new_saturating(self.0.saturating_pow(exp))
415412
}
416413

414+
/// Deserialization function for use with `#[serde(deserialize_with = ...)]` that performs
415+
/// deserialization through `f64` instead of `u64`.
416+
/// This allows deserializing from numbers with a fractional component like `.0`.
417+
///
418+
/// # Example
419+
/// ```rs
420+
/// use serde::Deserialize;
421+
/// use js_int::UInt;
422+
///
423+
/// #[derive(Deserialize)]
424+
/// struct Point {
425+
/// #[serde(deserialize_with = "UInt::deserialize_via_float")]
426+
/// x: UInt;
427+
/// #[serde(deserialize_with = "UInt::deserialize_via_float")]
428+
/// y: UInt;
429+
/// }
430+
/// ```
431+
#[cfg(feature = "serde")]
432+
pub fn deserialize_via_float<'de, D>(deserializer: D) -> Result<Self, D::Error>
433+
where
434+
D: Deserializer<'de>,
435+
{
436+
const EXPECTING: &str = "a number between 0 and 2^53 - 1 without fractional component";
437+
438+
let val = f64::deserialize(deserializer)?;
439+
440+
if val < 0.0 || val > MAX_SAFE_UINT as f64 || !super::is_acceptable_float(val) {
441+
Err(D::Error::invalid_value(Unexpected::Float(val), &EXPECTING))
442+
} else {
443+
Ok(Self(val as u64))
444+
}
445+
}
446+
417447
// TODO: wrapping_* methods, overflowing_* methods
418448
}
419449

@@ -571,30 +601,14 @@ impl<'de> Deserialize<'de> for UInt {
571601
where
572602
D: Deserializer<'de>,
573603
{
574-
#[cfg(not(feature = "float_deserialize"))]
575-
{
576-
let val = u64::deserialize(deserializer)?;
577-
578-
Self::new(val).ok_or_else(|| {
579-
D::Error::invalid_value(
580-
Unexpected::Unsigned(val),
581-
&"an integer between 0 and 2^53 - 1",
582-
)
583-
})
584-
}
585-
586-
#[cfg(feature = "float_deserialize")]
587-
{
588-
const EXPECTING: &str = "a number between 0 and 2^53 - 1 without fractional component";
589-
590-
let val = f64::deserialize(deserializer)?;
591-
592-
if val < 0.0 || val > MAX_SAFE_UINT as f64 || !super::is_acceptable_float(val) {
593-
Err(D::Error::invalid_value(Unexpected::Float(val), &EXPECTING))
594-
} else {
595-
Ok(Self(val as u64))
596-
}
597-
}
604+
let val = u64::deserialize(deserializer)?;
605+
606+
Self::new(val).ok_or_else(|| {
607+
D::Error::invalid_value(
608+
Unexpected::Unsigned(val),
609+
&"an integer between 0 and 2^53 - 1",
610+
)
611+
})
598612
}
599613
}
600614

tests/int.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ fn deserialize() {
2828
}
2929

3030
#[test]
31-
#[cfg_attr(feature = "float_deserialize", ignore)]
3231
fn dont_deserialize_integral_float() {
3332
assert!(deserialize_from(-10.0).is_err());
3433
assert!(deserialize_from(-0.0).is_err());
@@ -45,26 +44,31 @@ fn dont_deserialize_fractional_float() {
4544
}
4645

4746
#[test]
48-
#[cfg_attr(not(feature = "float_deserialize"), ignore)]
4947
fn deserialize_integral_float() {
50-
assert_eq!(deserialize_from(-10.0).unwrap(), int!(-10));
51-
assert_eq!(deserialize_from(-0.0).unwrap(), int!(0));
52-
assert_eq!(deserialize_from(1.0).unwrap(), int!(1));
53-
assert_eq!(deserialize_from(9007199254740991.0).unwrap(), Int::MAX);
54-
assert!(deserialize_from(9007199254740992.0).is_err());
48+
assert_eq!(deserialize_via_float(-10.0).unwrap(), int!(-10));
49+
assert_eq!(deserialize_via_float(-0.0).unwrap(), int!(0));
50+
assert_eq!(deserialize_via_float(1.0).unwrap(), int!(1));
51+
assert_eq!(deserialize_via_float(9007199254740991.0).unwrap(), Int::MAX);
52+
assert!(deserialize_via_float(9007199254740992.0).is_err());
5553
// NOTE: This still ends up as integral because the .49 exceeds the representable range of f64
5654
assert_eq!(
57-
deserialize_from(9007199254740991.49).unwrap(),
55+
deserialize_via_float(9007199254740991.49).unwrap(),
5856
Int::try_from(9007199254740991i64).unwrap()
5957
);
6058

61-
assert!(deserialize_from(f64::NAN).is_err());
62-
assert!(deserialize_from(f64::INFINITY).is_err());
63-
assert!(deserialize_from(f64::NEG_INFINITY).is_err());
59+
assert!(deserialize_via_float(f64::NAN).is_err());
60+
assert!(deserialize_via_float(f64::INFINITY).is_err());
61+
assert!(deserialize_via_float(f64::NEG_INFINITY).is_err());
62+
63+
fn deserialize_via_float<'de, Value: IntoDeserializer<'de>>(
64+
value: Value,
65+
) -> Result<Int, serde::de::value::Error> {
66+
Int::deserialize_via_float(value.into_deserializer())
67+
}
6468
}
6569

6670
fn deserialize_from<'de, Value: IntoDeserializer<'de>>(
6771
value: Value,
6872
) -> Result<Int, serde::de::value::Error> {
6973
Int::deserialize(value.into_deserializer())
70-
}
74+
}

tests/uint.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ fn deserialize() {
2424
}
2525

2626
#[test]
27-
#[cfg_attr(feature = "float_deserialize", ignore)]
2827
fn dont_deserialize_integral_float() {
2928
assert!(deserialize_from(1.0).is_err());
3029
assert!(deserialize_from(9007199254740991.0).is_err());
@@ -38,20 +37,24 @@ fn dont_deserialize_fractional_float() {
3837
}
3938

4039
#[test]
41-
#[cfg_attr(not(feature = "float_deserialize"), ignore)]
4240
fn deserialize_integral_float() {
43-
assert_eq!(deserialize_from(1.0).unwrap(), uint!(1));
44-
assert_eq!(deserialize_from(9007199254740991.0).unwrap(), UInt::MAX);
45-
assert!(deserialize_from(9007199254740992.0).is_err());
41+
assert_eq!(deserialize_via_float(1.0).unwrap(), uint!(1));
42+
assert_eq!(deserialize_via_float(9007199254740991.0).unwrap(), UInt::MAX);
43+
assert!(deserialize_via_float(9007199254740992.0).is_err());
4644
// NOTE: This still ends up as integral because the .49 exceeds the representable range of f64
4745
assert_eq!(
48-
deserialize_from(9007199254740991.49).unwrap(),
46+
deserialize_via_float(9007199254740991.49).unwrap(),
4947
UInt::try_from(9007199254740991i64).unwrap()
5048
);
5149

52-
assert!(deserialize_from(f64::NAN).is_err());
53-
assert!(deserialize_from(f64::INFINITY).is_err());
54-
assert!(deserialize_from(f64::NEG_INFINITY).is_err());
50+
assert!(deserialize_via_float(f64::NAN).is_err());
51+
assert!(deserialize_via_float(f64::INFINITY).is_err());
52+
assert!(deserialize_via_float(f64::NEG_INFINITY).is_err());
53+
54+
fn deserialize_via_float<'de, Value: IntoDeserializer<'de>>(value: Value) -> Result<UInt, serde::de::value::Error>
55+
{
56+
UInt::deserialize_via_float(value.into_deserializer())
57+
}
5558
}
5659

5760
fn deserialize_from<'de, Value: IntoDeserializer<'de>>(

0 commit comments

Comments
 (0)