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::Rng;
9use rand_chacha::rand_core::CryptoRngCore;
10use ulid::Ulid;
11
12use crate::clock::Clock;
13
14/// A boxed [`Clock`]
15pub type BoxClock = Box<dyn Clock + Send>;
16/// A boxed random number generator
17pub type BoxRng = Box<dyn CryptoRngCore + Send>;
18
19/// Extension trait on [`Ulid`] to build and inspect ULIDs using `chrono`
20/// timestamps and an injected RNG.
21pub trait UlidExt: Sized {
22    /// Generate a [`Ulid`] for the given timestamp, sourcing its randomness
23    /// from `rng`.
24    ///
25    /// This reproduces the exact bit layout of `ulid`'s own
26    /// `Ulid::from_datetime_with_source` (48 timestamp bits, then 80 random
27    /// bits drawn as a `u16` followed by a `u64`).
28    fn from_datetime_with_rng<R: Rng + ?Sized>(datetime: DateTime<Utc>, rng: &mut R) -> Self;
29
30    /// The creation timestamp encoded in this [`Ulid`], as a `chrono` datetime.
31    fn datetime_utc(&self) -> DateTime<Utc>;
32}
33
34impl UlidExt for Ulid {
35    fn from_datetime_with_rng<R: Rng + ?Sized>(datetime: DateTime<Utc>, rng: &mut R) -> Self {
36        let timestamp_ms = u64::try_from(datetime.timestamp_millis()).unwrap_or(0);
37        let timebits = timestamp_ms & ((1 << Ulid::TIME_BITS) - 1);
38
39        let msb = timebits << 16 | u64::from(rng.r#gen::<u16>());
40        let lsb = rng.r#gen::<u64>();
41        Ulid::from(u128::from(msb) << 64 | u128::from(lsb))
42    }
43
44    fn datetime_utc(&self) -> DateTime<Utc> {
45        DateTime::from_timestamp_millis(i64::try_from(self.timestamp_ms()).unwrap_or(i64::MAX))
46            .unwrap_or_default()
47    }
48}