Skip to main content

mas_data_model/
utils.rs

1// Copyright 2025, 2026 Element Creations Ltd.
2// Copyright 2025 New Vector Ltd.
3//
4// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5// Please see LICENSE files in the repository root for full details.
6
7use chrono::{DateTime, Utc};
8use rand::{CryptoRng, RngExt};
9use ulid::Ulid;
10
11use crate::clock::Clock;
12
13/// A boxed [`Clock`]
14pub type BoxClock = Box<dyn Clock + Send>;
15/// A boxed random number generator
16pub type BoxRng = Box<dyn CryptoRng + Send>;
17
18/// Extension trait on [`Ulid`] to build and inspect ULIDs using `chrono`
19/// timestamps and an injected RNG.
20pub trait UlidExt: Sized {
21    /// Generate a [`Ulid`] for the given timestamp, sourcing its randomness
22    /// from `rng`.
23    ///
24    /// This reproduces the exact bit layout of `ulid`'s own
25    /// `Ulid::from_datetime_with_source` (48 timestamp bits, then 80 random
26    /// bits drawn as a `u16` followed by a `u64`).
27    fn from_datetime_with_rng<R: RngExt + ?Sized>(datetime: DateTime<Utc>, rng: &mut R) -> Self;
28
29    /// The creation timestamp encoded in this [`Ulid`], as a `chrono` datetime.
30    fn datetime_utc(&self) -> DateTime<Utc>;
31}
32
33impl UlidExt for Ulid {
34    fn from_datetime_with_rng<R: RngExt + ?Sized>(datetime: DateTime<Utc>, rng: &mut R) -> Self {
35        let timestamp_ms = u64::try_from(datetime.timestamp_millis()).unwrap_or(0);
36        let timebits = timestamp_ms & ((1 << Ulid::TIME_BITS) - 1);
37
38        let msb = timebits << 16 | u64::from(rng.random::<u16>());
39        let lsb = rng.random::<u64>();
40        Ulid::from(u128::from(msb) << 64 | u128::from(lsb))
41    }
42
43    fn datetime_utc(&self) -> DateTime<Utc> {
44        DateTime::from_timestamp_millis(i64::try_from(self.timestamp_ms()).unwrap_or(i64::MAX))
45            .unwrap_or_default()
46    }
47}