Skip to content

Commit

Permalink
Simplify SerdeError
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Feb 24, 2024
1 parent 02c68d6 commit fda69d1
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 98 deletions.
71 changes: 29 additions & 42 deletions src/datetime/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{de, ser};

use super::DateTime;
use crate::format::{write_rfc3339, SecondsFormat};
use crate::naive::datetime::serde::serde_from;
use crate::naive::datetime::serde::invalid_ts;
#[cfg(feature = "clock")]
use crate::offset::Local;
use crate::offset::{FixedOffset, Offset, TimeZone, Utc};
Expand Down Expand Up @@ -148,10 +148,9 @@ pub mod ts_nanoseconds {
use core::fmt;
use serde::{de, ser};

use crate::offset::TimeZone;
use crate::{DateTime, Utc};

use super::{serde_from, NanoSecondsTimestampVisitor};
use super::{invalid_ts, NanoSecondsTimestampVisitor};

/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
///
Expand Down Expand Up @@ -240,24 +239,20 @@ pub mod ts_nanoseconds {
where
E: de::Error,
{
serde_from(
Utc.timestamp_opt(
value.div_euclid(1_000_000_000),
(value.rem_euclid(1_000_000_000)) as u32,
),
&value,
DateTime::from_timestamp(
value.div_euclid(1_000_000_000),
(value.rem_euclid(1_000_000_000)) as u32,
)
.ok_or_else(|| invalid_ts(value))
}

/// Deserialize a timestamp in nanoseconds since the epoch
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
serde_from(
Utc.timestamp_opt((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32),
&value,
)
DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32)
.ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -448,8 +443,7 @@ pub mod ts_microseconds {
use core::fmt;
use serde::{de, ser};

use super::{serde_from, MicroSecondsTimestampVisitor};
use crate::offset::TimeZone;
use super::{invalid_ts, MicroSecondsTimestampVisitor};
use crate::{DateTime, Utc};

/// Serialize a UTC datetime into an integer number of microseconds since the epoch
Expand Down Expand Up @@ -529,24 +523,23 @@ pub mod ts_microseconds {
where
E: de::Error,
{
serde_from(
Utc.timestamp_opt(
value.div_euclid(1_000_000),
(value.rem_euclid(1_000_000) * 1_000) as u32,
),
&value,
DateTime::from_timestamp(
value.div_euclid(1_000_000),
(value.rem_euclid(1_000_000) * 1000) as u32,
)
.ok_or_else(|| invalid_ts(value))
}

/// Deserialize a timestamp in milliseconds since the epoch
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
serde_from(
Utc.timestamp_opt((value / 1_000_000) as i64, ((value % 1_000_000) * 1_000) as u32),
&value,
DateTime::from_timestamp(
(value / 1_000_000) as i64,
((value % 1_000_000) * 1_000) as u32,
)
.ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -726,8 +719,7 @@ pub mod ts_milliseconds {
use core::fmt;
use serde::{de, ser};

use super::{serde_from, MilliSecondsTimestampVisitor};
use crate::offset::TimeZone;
use super::{invalid_ts, MilliSecondsTimestampVisitor};
use crate::{DateTime, Utc};

/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
Expand Down Expand Up @@ -807,18 +799,16 @@ pub mod ts_milliseconds {
where
E: de::Error,
{
serde_from(Utc.timestamp_millis_opt(value), &value)
DateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value))
}

/// Deserialize a timestamp in milliseconds since the epoch
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
serde_from(
Utc.timestamp_opt((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32),
&value,
)
DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32)
.ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -1005,8 +995,8 @@ pub mod ts_seconds {
use core::fmt;
use serde::{de, ser};

use super::{serde_from, SecondsTimestampVisitor};
use crate::{DateTime, LocalResult, TimeZone, Utc};
use super::{invalid_ts, SecondsTimestampVisitor};
use crate::{DateTime, Utc};

/// Serialize a UTC datetime into an integer number of seconds since the epoch
///
Expand Down Expand Up @@ -1075,22 +1065,19 @@ pub mod ts_seconds {
where
E: de::Error,
{
serde_from(Utc.timestamp_opt(value, 0), &value)
DateTime::from_timestamp(value, 0).ok_or_else(|| invalid_ts(value))
}

/// Deserialize a timestamp in seconds since the epoch
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
serde_from(
if value > i64::MAX as u64 {
LocalResult::None
} else {
Utc.timestamp_opt(value as i64, 0)
},
&value,
)
if value > i64::MAX as u64 {
Err(invalid_ts(value))
} else {
DateTime::from_timestamp(value as i64, 0).ok_or_else(|| invalid_ts(value))
}
}
}
}
Expand Down
82 changes: 26 additions & 56 deletions src/naive/datetime/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use core::fmt;
use serde::{de, ser};

use super::NaiveDateTime;
use crate::offset::LocalResult;

/// Serialize a `NaiveDateTime` as an RFC 3339 string
///
Expand Down Expand Up @@ -83,7 +82,7 @@ pub mod ts_nanoseconds {
use core::fmt;
use serde::{de, ser};

use super::ne_timestamp;
use super::invalid_ts;
use crate::NaiveDateTime;

/// Serialize a datetime into an integer number of nanoseconds since the epoch
Expand Down Expand Up @@ -176,7 +175,7 @@ pub mod ts_nanoseconds {
value.div_euclid(1_000_000_000),
(value.rem_euclid(1_000_000_000)) as u32,
)
.ok_or_else(|| E::custom(ne_timestamp(value)))
.ok_or_else(|| invalid_ts(value))
}

fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
Expand All @@ -187,7 +186,7 @@ pub mod ts_nanoseconds {
(value / 1_000_000_000) as i64,
(value % 1_000_000_000) as u32,
)
.ok_or_else(|| E::custom(ne_timestamp(value)))
.ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -372,7 +371,7 @@ pub mod ts_microseconds {
use core::fmt;
use serde::{de, ser};

use super::ne_timestamp;
use super::invalid_ts;
use crate::NaiveDateTime;

/// Serialize a datetime into an integer number of microseconds since the epoch
Expand Down Expand Up @@ -451,8 +450,7 @@ pub mod ts_microseconds {
where
E: de::Error,
{
NaiveDateTime::from_timestamp_micros(value)
.ok_or_else(|| E::custom(ne_timestamp(value)))
NaiveDateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value))
}

fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
Expand All @@ -463,7 +461,7 @@ pub mod ts_microseconds {
(value / 1_000_000) as i64,
((value % 1_000_000) * 1_000) as u32,
)
.ok_or_else(|| E::custom(ne_timestamp(value)))
.ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -636,7 +634,7 @@ pub mod ts_milliseconds {
use core::fmt;
use serde::{de, ser};

use super::ne_timestamp;
use super::invalid_ts;
use crate::NaiveDateTime;

/// Serialize a datetime into an integer number of milliseconds since the epoch
Expand Down Expand Up @@ -715,8 +713,7 @@ pub mod ts_milliseconds {
where
E: de::Error,
{
NaiveDateTime::from_timestamp_millis(value)
.ok_or_else(|| E::custom(ne_timestamp(value)))
NaiveDateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value))
}

fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
Expand All @@ -727,7 +724,7 @@ pub mod ts_milliseconds {
(value / 1000) as i64,
((value % 1000) * 1_000_000) as u32,
)
.ok_or_else(|| E::custom(ne_timestamp(value)))
.ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -896,7 +893,7 @@ pub mod ts_seconds {
use core::fmt;
use serde::{de, ser};

use super::ne_timestamp;
use super::invalid_ts;
use crate::NaiveDateTime;

/// Serialize a datetime into an integer number of seconds since the epoch
Expand Down Expand Up @@ -968,16 +965,14 @@ pub mod ts_seconds {
where
E: de::Error,
{
NaiveDateTime::from_timestamp_opt(value, 0)
.ok_or_else(|| E::custom(ne_timestamp(value)))
NaiveDateTime::from_timestamp_opt(value, 0).ok_or_else(|| invalid_ts(value))
}

fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
NaiveDateTime::from_timestamp_opt(value as i64, 0)
.ok_or_else(|| E::custom(ne_timestamp(value)))
NaiveDateTime::from_timestamp_opt(value as i64, 0).ok_or_else(|| invalid_ts(value))
}
}
}
Expand Down Expand Up @@ -1109,54 +1104,29 @@ pub mod ts_seconds_option {
}
}

// lik? function to convert a LocalResult into a serde-ish Result
pub(crate) fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E>
where
E: de::Error,
V: fmt::Display,
T: fmt::Display,
{
match me {
LocalResult::None => Err(E::custom(ne_timestamp(ts))),
LocalResult::Ambiguous(min, max) => {
Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min, max }))
}
LocalResult::Single(val) => Ok(val),
}
}

enum SerdeError<V: fmt::Display, D: fmt::Display> {
NonExistent { timestamp: V },
Ambiguous { timestamp: V, min: D, max: D },
}

/// Construct a [`SerdeError::NonExistent`]
fn ne_timestamp<T: fmt::Display>(ts: T) -> SerdeError<T, u8> {
SerdeError::NonExistent::<T, u8> { timestamp: ts }
pub(crate) enum SerdeError<T: fmt::Display> {
InvalidTimestamp(T),
}

impl<V: fmt::Display, D: fmt::Display> fmt::Debug for SerdeError<V, D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ChronoSerdeError({})", self)
}
}

// impl<V: fmt::Display, D: fmt::Debug> core::error::Error for SerdeError<V, D> {}
impl<V: fmt::Display, D: fmt::Display> fmt::Display for SerdeError<V, D> {
impl<T: fmt::Display> fmt::Display for SerdeError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SerdeError::NonExistent { timestamp } => {
write!(f, "value is not a legal timestamp: {}", timestamp)
SerdeError::InvalidTimestamp(ts) => {
write!(f, "value is not a legal timestamp: {}", ts)
}
SerdeError::Ambiguous { timestamp, min, max } => write!(
f,
"value is an ambiguous timestamp: {}, could be either of {}, {}",
timestamp, min, max
),
}
}
}

/// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`.
pub(crate) fn invalid_ts<E, T>(value: T) -> E
where
E: de::Error,
T: fmt::Display,
{
E::custom(SerdeError::InvalidTimestamp(value))
}

#[cfg(test)]
mod tests {
use crate::naive::datetime::{test_decodable_json, test_encodable_json};
Expand Down

0 comments on commit fda69d1

Please sign in to comment.