use std::fmt;
use std::time::{Duration, SystemTime};
use crate::Ulid;
#[derive(Debug, Clone)]
pub struct Generator {
previous: Ulid,
}
impl Generator {
pub const fn new() -> Generator {
Generator {
previous: Ulid::nil(),
}
}
pub fn generate(&mut self) -> Result<Ulid, Overflow<'_>> {
self.generate_from_datetime(crate::time_utils::now())
}
pub fn generate_from_datetime(&mut self, datetime: SystemTime) -> Result<Ulid, Overflow<'_>> {
self.generate_from_datetime_with_source(datetime, &mut rand::rng())
}
pub fn generate_with_source<R>(&mut self, source: &mut R) -> Result<Ulid, Overflow<'_>>
where
R: rand::Rng + ?Sized,
{
self.generate_from_datetime_with_source(crate::time_utils::now(), source)
}
pub fn generate_from_datetime_with_source<R>(
&mut self,
datetime: SystemTime,
source: &mut R,
) -> Result<Ulid, Overflow<'_>>
where
R: rand::Rng + ?Sized,
{
let last_ms = self.previous.timestamp_ms();
if datetime
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_millis()
<= u128::from(last_ms)
{
if let Ok(next) = self.previous.increment() {
self.previous = next;
return Ok(next);
} else {
return Err(Overflow { generator: self });
}
}
let next = Ulid::from_datetime_with_source(datetime, source);
self.previous = next;
Ok(next)
}
}
impl Default for Generator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Overflow<'a> {
generator: &'a mut Generator,
}
impl<'a> Overflow<'a> {
pub fn commit_overflow_increment(self) -> Ulid {
let next = match self.generator.previous.increment() {
Ok(next) => next,
Err(next) => next,
};
self.generator.previous = next;
next
}
pub fn commit_overflow_random(self) -> Ulid {
self.commit_overflow_random_with_source(&mut rand::rng())
}
pub fn commit_overflow_random_with_source<R>(self, source: &mut R) -> Ulid
where
R: rand::Rng,
{
let inc = match self.generator.previous.increment() {
Ok(inc) => inc,
Err(inc) => inc,
};
let next = Ulid::from_datetime_with_source(inc.datetime(), source);
self.generator.previous = next;
next
}
}
impl std::error::Error for Overflow<'_> {}
impl fmt::Display for Overflow<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "Ulid random bits would overflow")
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_order_monotonic() {
let dt = SystemTime::now();
let mut gen = Generator::new();
let ulid1 = gen.generate_from_datetime(dt).unwrap();
let ulid2 = gen.generate_from_datetime(dt).unwrap();
let ulid3 = Ulid::from_datetime(dt + Duration::from_millis(1));
assert_eq!(ulid1.0 + 1, ulid2.0);
assert!(ulid2 < ulid3);
assert!(ulid2.timestamp_ms() < ulid3.timestamp_ms())
}
#[test]
fn test_order_monotonic_with_source() {
let mut source = crate::StepRng::new(123, 0);
let mut gen = Generator::new();
let _has_default = Generator::default();
let ulid1 = gen.generate_with_source(&mut source).unwrap();
let ulid2 = gen.generate_with_source(&mut source).unwrap();
assert!(ulid1 < ulid2);
}
#[test]
fn test_can_display_overflow() {
let generator = &mut Generator::new();
println!("{}", Overflow { generator });
}
#[test]
fn test_overflow_commit_increment() {
let maxed_random = {
let ulid = Ulid::gen();
Ulid::from_parts(
ulid.timestamp_ms(),
u128::MAX & crate::bitmask!(Ulid::RAND_BITS),
)
};
let mut gen = Generator {
previous: maxed_random,
};
let err_overflow = gen
.generate_from_datetime(maxed_random.datetime())
.unwrap_err();
let next_ulid = err_overflow.commit_overflow_increment();
assert_eq!(next_ulid.timestamp_ms(), maxed_random.timestamp_ms() + 1);
assert_eq!(next_ulid.random(), 0);
assert_eq!(next_ulid.0, maxed_random.0 + 1);
assert_eq!(gen.previous, next_ulid);
}
#[test]
fn test_overflow_commit_random() {
let maxed_random = {
let ulid = Ulid::gen();
Ulid::from_parts(
ulid.timestamp_ms(),
u128::MAX & crate::bitmask!(Ulid::RAND_BITS),
)
};
let mut gen = Generator {
previous: maxed_random,
};
let err_overflow = gen
.generate_from_datetime(maxed_random.datetime())
.unwrap_err();
let next_ulid = err_overflow.commit_overflow_random();
assert_eq!(next_ulid.timestamp_ms(), maxed_random.timestamp_ms() + 1);
assert_ne!(next_ulid.random(), 0);
assert!(next_ulid > maxed_random);
assert_eq!(gen.previous, next_ulid);
}
#[test]
fn test_overflow_commit_random_with_source() {
let maxed_random = {
let ulid = Ulid::gen();
Ulid::from_parts(
ulid.timestamp_ms(),
u128::MAX & crate::bitmask!(Ulid::RAND_BITS),
)
};
let mut gen = Generator {
previous: maxed_random,
};
let err_overflow = gen
.generate_from_datetime(maxed_random.datetime())
.unwrap_err();
let mut source = crate::StepRng::new(42, 0);
let next_ulid = err_overflow.commit_overflow_random_with_source(&mut source);
assert_eq!(next_ulid.timestamp_ms(), maxed_random.timestamp_ms() + 1);
assert_eq!(next_ulid.random(), 42 << 64 | 42);
assert!(next_ulid > maxed_random);
assert_eq!(gen.previous, next_ulid);
}
}