Skip to main content

tlb_ton/
timestamp.rs

1use chrono::{DateTime, Utc};
2use tlb::{
3    Error,
4    bits::{
5        de::{BitReader, BitReaderExt, BitUnpackAs},
6        ser::{BitPackAs, BitWriter, BitWriterExt},
7    },
8};
9
10/// Adapter to **de**/**ser**ialize UNIX timestamp as `u32` from [`DateTime`]
11pub struct UnixTimestamp;
12
13#[cfg(feature = "arbitrary")]
14impl UnixTimestamp {
15    #[inline]
16    pub fn arbitrary(u: &mut ::arbitrary::Unstructured) -> ::arbitrary::Result<DateTime<Utc>> {
17        Ok(DateTime::from_timestamp(
18            u.int_in_range(
19                DateTime::UNIX_EPOCH.timestamp()..=DateTime::<Utc>::MAX_UTC.timestamp(),
20            )?,
21            0,
22        )
23        .unwrap_or_else(|| unreachable!()))
24    }
25
26    #[inline]
27    pub fn arbitrary_option(
28        u: &mut ::arbitrary::Unstructured,
29    ) -> ::arbitrary::Result<Option<DateTime<Utc>>> {
30        use arbitrary::Arbitrary;
31
32        Option::<()>::arbitrary(u)?
33            .map(|()| Self::arbitrary(u))
34            .transpose()
35    }
36}
37
38impl BitPackAs<DateTime<Utc>> for UnixTimestamp {
39    type Args = ();
40
41    #[inline]
42    fn pack_as<W>(source: &DateTime<Utc>, writer: &mut W, _: Self::Args) -> Result<(), W::Error>
43    where
44        W: BitWriter + ?Sized,
45    {
46        let timestamp: u32 = source
47            .timestamp()
48            .try_into()
49            .map_err(|_| Error::custom("timestamp: overflow"))?;
50        writer.pack(timestamp, ())?;
51        Ok(())
52    }
53}
54
55impl<'de> BitUnpackAs<'de, DateTime<Utc>> for UnixTimestamp {
56    type Args = ();
57
58    #[inline]
59    fn unpack_as<R>(reader: &mut R, _: Self::Args) -> Result<DateTime<Utc>, R::Error>
60    where
61        R: BitReader<'de> + ?Sized,
62    {
63        let timestamp: u32 = reader.unpack(())?;
64        DateTime::from_timestamp(timestamp as i64, 0)
65            .ok_or_else(|| Error::custom("timestamp: overflow"))
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use tlb::bits::{de::unpack_fully_as, ser::pack_as};
72
73    use super::*;
74
75    #[test]
76    fn unix_timestamp_serde() {
77        let ts = DateTime::UNIX_EPOCH;
78
79        let packed = pack_as::<_, UnixTimestamp>(ts, ()).unwrap();
80        let got: DateTime<Utc> = unpack_fully_as::<_, UnixTimestamp>(&packed, ()).unwrap();
81
82        assert_eq!(got, ts);
83    }
84}