From 4dd2fb5d969eaf45e66f584684705b9cf5842e28 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Thu, 12 Sep 2024 21:50:26 +0200 Subject: [PATCH] WIP --- Cargo.lock | 1 + serde_with/Cargo.toml | 1 + serde_with/src/chrono_0_4.rs | 10 +++++++--- serde_with/src/utils.rs | 23 +++++++++++------------ serde_with/src/utils/duration.rs | 3 ++- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a969cd0a..871c1544 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,7 @@ dependencies = [ "indexmap 2.7.0", "jsonschema", "mime", + "num-traits", "pretty_assertions", "regex", "rmp-serde", diff --git a/serde_with/Cargo.toml b/serde_with/Cargo.toml index 1f0034ed..b335ba80 100644 --- a/serde_with/Cargo.toml +++ b/serde_with/Cargo.toml @@ -136,6 +136,7 @@ hashbrown_0_15 = { package = "hashbrown", version = "0.15.0", optional = true, d hex = { version = "0.4.3", optional = true, default-features = false } indexmap_1 = { package = "indexmap", version = "1.8", optional = true, default-features = false, features = ["serde-1"] } indexmap_2 = { package = "indexmap", version = "2.0", optional = true, default-features = false, features = ["serde"] } +num-traits = { version = "0.2.19", features = ["std"] } schemars_0_8 = { package = "schemars", version = "0.8.16", optional = true, default-features = false } serde = { version = "1.0.152", default-features = false } serde_derive = "1.0.152" diff --git a/serde_with/src/chrono_0_4.rs b/serde_with/src/chrono_0_4.rs index 7be9c136..7e8f4c93 100644 --- a/serde_with/src/chrono_0_4.rs +++ b/serde_with/src/chrono_0_4.rs @@ -62,6 +62,7 @@ fn unix_epoch_naive() -> NaiveDateTime { #[cfg(feature = "std")] pub mod datetime_utc_ts_seconds_from_any { use super::*; + use num_traits::ToPrimitive as _; /// Deserialize a Unix timestamp with optional subsecond precision into a `DateTime`. pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> @@ -110,9 +111,12 @@ pub mod datetime_utc_ts_seconds_from_any { where E: DeError, { - let seconds = value.trunc() as i64; - let nsecs = (value.fract() * 1_000_000_000_f64).abs() as u32; - DateTime::from_timestamp(seconds, nsecs).ok_or_else(|| { + fn f64_to_value(value: f64) -> Option> { + let seconds = value.trunc().to_i64()?; + let nsecs = (value.fract() * 1_000_000_000_f64).abs().to_u32()?; + DateTime::from_timestamp(seconds, nsecs) + } + f64_to_value(value).ok_or_else(|| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) diff --git a/serde_with/src/utils.rs b/serde_with/src/utils.rs index 6d895ef7..8ce65c4b 100644 --- a/serde_with/src/utils.rs +++ b/serde_with/src/utils.rs @@ -1,6 +1,7 @@ pub(crate) mod duration; use crate::prelude::*; +use num_traits::ToPrimitive as _; /// Re-Implementation of `serde::private::de::size_hint::cautious` #[cfg(feature = "alloc")] @@ -40,7 +41,10 @@ pub(crate) const NANOS_PER_SEC_F64: f64 = 1_000_000_000.0; // pub(crate) const NANOS_PER_MICRO: u32 = 1_000; // pub(crate) const MILLIS_PER_SEC: u64 = 1_000; // pub(crate) const MICROS_PER_SEC: u64 = 1_000_000; +#[expect(clippy::as_conversions)] pub(crate) const U64_MAX: u128 = u64::MAX as u128; +#[expect(clippy::as_conversions)] +pub(crate) const U64_MAX_FLOAT: f64 = u64::MAX as f64; pub(crate) struct MapIter<'de, A, K, V> { pub(crate) access: A, @@ -115,27 +119,22 @@ where } } -pub(crate) fn duration_signed_from_secs_f64(secs: f64) -> Result { - const MAX_NANOS_F64: f64 = ((U64_MAX + 1) * NANOS_PER_SEC) as f64; - // TODO why are the seconds converted to nanoseconds first? - // Does it make sense to just truncate the value? - let mut nanos = secs * NANOS_PER_SEC_F64; - if !nanos.is_finite() { +pub(crate) fn duration_signed_from_secs_f64(mut secs: f64) -> Result { + if !secs.is_finite() { return Err("got non-finite value when converting float to duration"); } - if nanos >= MAX_NANOS_F64 { + if secs.trunc() > U64_MAX_FLOAT { return Err("overflow when converting float to duration"); } let mut sign = Sign::Positive; - if nanos < 0.0 { - nanos = -nanos; + if secs < 0.0 { + secs = -secs; sign = Sign::Negative; } - let nanos = nanos as u128; Ok(DurationSigned::new( sign, - (nanos / NANOS_PER_SEC) as u64, - (nanos % NANOS_PER_SEC) as u32, + secs.trunc() as u64, + (secs.fract() * NANOS_PER_SEC_F64) as u32, )) } diff --git a/serde_with/src/utils/duration.rs b/serde_with/src/utils/duration.rs index dca876ad..0b6f834b 100644 --- a/serde_with/src/utils/duration.rs +++ b/serde_with/src/utils/duration.rs @@ -9,6 +9,7 @@ use crate::{ formats::{Flexible, Format, Strict, Strictness}, prelude::*, }; +use num_traits::ToPrimitive as _; #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(test, derive(Debug))] @@ -212,7 +213,7 @@ where where S: Serializer, { - let mut secs = source + let mut secs: i64 = source .sign .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| { SerError::custom("The Duration of Timestamp is outside the supported range.")