1use chrono::{DateTime, Utc};
2use tlb::{
3 Error,
4 bits::{
5 de::{BitReader, BitReaderExt, r#as::BitUnpackAs},
6 ser::{BitWriter, BitWriterExt, r#as::BitPackAs},
7 },
8};
9
10pub 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 #[inline]
40 fn pack_as<W>(source: &DateTime<Utc>, mut writer: W) -> Result<(), W::Error>
41 where
42 W: BitWriter,
43 {
44 let timestamp: u32 = source
45 .timestamp()
46 .try_into()
47 .map_err(|_| Error::custom("timestamp: overflow"))?;
48 writer.pack(timestamp)?;
49 Ok(())
50 }
51}
52
53impl<'de> BitUnpackAs<'de, DateTime<Utc>> for UnixTimestamp {
54 #[inline]
55 fn unpack_as<R>(mut reader: R) -> Result<DateTime<Utc>, R::Error>
56 where
57 R: BitReader<'de>,
58 {
59 let timestamp: u32 = reader.unpack()?;
60 DateTime::from_timestamp(timestamp as i64, 0)
61 .ok_or_else(|| Error::custom("timestamp: overflow"))
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use tlb::bits::{de::r#as::unpack_fully_as, ser::r#as::pack_as};
68
69 use super::*;
70
71 #[test]
72 fn unix_timestamp_serde() {
73 let ts = DateTime::UNIX_EPOCH;
74
75 let packed = pack_as::<_, UnixTimestamp>(ts).unwrap();
76 let got: DateTime<Utc> = unpack_fully_as::<_, UnixTimestamp>(&packed).unwrap();
77
78 assert_eq!(got, ts);
79 }
80}