1use chrono::{DateTime, Utc};
2use tlb::{
3 Error,
4 bits::{
5 de::{BitReader, BitReaderExt, BitUnpackAs},
6 ser::{BitPackAs, BitWriter, BitWriterExt},
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 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}