Skip to main content

bacnet_time/
convert.rs

1//! Conversions to and from `core::time::Duration`.
2
3use core::fmt;
4
5use crate::divide::div_rem;
6use crate::{Duration, NANOS_PER_SEC};
7
8/// A `core::time::Duration` too long for a [`Duration`]: more than
9/// `u64::MAX` nanoseconds, about 584 years. What
10/// `Duration::try_from(core::time::Duration)` returns for one;
11/// [`Duration::saturating_from_core`] clamps it instead.
12///
13/// Only the conversion builds one, as with the standard library's
14/// `TryFromIntError`, so the type can carry more detail later.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct DurationTooLong(());
17
18impl fmt::Display for DurationTooLong {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.write_str("duration longer than u64::MAX nanoseconds (about 584 years)")
21    }
22}
23
24impl core::error::Error for DurationTooLong {}
25
26impl Duration {
27    /// `duration` in nanoseconds, or `None` past [`Duration::MAX`].
28    const fn checked_from_core(duration: core::time::Duration) -> Option<Self> {
29        match duration.as_secs().checked_mul(NANOS_PER_SEC) {
30            Some(whole) => match whole.checked_add(duration.subsec_nanos() as u64) {
31                Some(nanos) => Some(Self::from_nanos(nanos)),
32                None => None,
33            },
34            None => None,
35        }
36    }
37
38    /// `duration`, clamped at [`Duration::MAX`], about 584 years: for a
39    /// host mapping its own clock onto the timeline, where a time that far
40    /// out means never. A length that must fit, from configuration say,
41    /// goes through `Duration::try_from` instead, which refuses one too long.
42    #[must_use]
43    pub const fn saturating_from_core(duration: core::time::Duration) -> Self {
44        match Self::checked_from_core(duration) {
45            Some(duration) => duration,
46            None => Self::MAX,
47        }
48    }
49}
50
51impl TryFrom<core::time::Duration> for Duration {
52    type Error = DurationTooLong;
53
54    /// The same length, or [`DurationTooLong`] for one past
55    /// [`Duration::MAX`]: a `core::time::Duration` reaches about 584
56    /// billion years.
57    fn try_from(duration: core::time::Duration) -> Result<Self, DurationTooLong> {
58        Self::checked_from_core(duration).ok_or(DurationTooLong(()))
59    }
60}
61
62impl From<Duration> for core::time::Duration {
63    /// The same length, exactly. Splitting it into seconds and nanoseconds
64    /// takes no 64-bit division routine (see the
65    /// [crate docs](crate#no-64-bit-division)).
66    fn from(duration: Duration) -> Self {
67        let (secs, nanos) = div_rem(duration.as_nanos(), NANOS_PER_SEC);
68        // The remainder is under one second, so it fits a u32.
69        Self::new(secs, nanos as u32)
70    }
71}