use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TimestampMillis(u128);
impl TimestampMillis {
pub fn now() -> Self {
let millis = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
Self(millis)
}
#[cfg(test)]
pub(crate) fn from_millis(millis: u128) -> Self {
Self(millis)
}
pub fn age_since_now(&self) -> Duration {
let now_millis = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
let age_millis = now_millis.saturating_sub(self.0);
Duration::from_millis(age_millis as u64)
}
pub fn is_older_than(&self, duration: Duration) -> bool {
self.age_since_now() > duration
}
}
impl Default for TimestampMillis {
fn default() -> Self {
Self::now()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct AccessSequence(u64);
impl AccessSequence {
pub fn next(&self) -> Self {
Self(self.0.saturating_add(1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timestamp_millis_ordering() {
let t1 = TimestampMillis::from_millis(1000);
let t2 = TimestampMillis::from_millis(2000);
assert!(t1 < t2);
assert!(t2 > t1);
assert_eq!(t1, t1);
}
#[test]
fn timestamp_millis_age() {
let past = TimestampMillis::from_millis(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis()
- 5000,
);
let age = past.age_since_now();
assert!(age >= Duration::from_millis(5000));
assert!(age < Duration::from_millis(6000));
}
#[test]
fn timestamp_millis_age_future() {
let future = TimestampMillis::from_millis(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis()
+ 5000,
);
let age = future.age_since_now();
assert_eq!(age, Duration::ZERO);
}
#[test]
fn timestamp_millis_is_older_than() {
let past = TimestampMillis::from_millis(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis()
- 5000,
);
assert!(past.is_older_than(Duration::from_millis(4000)));
assert!(!past.is_older_than(Duration::from_millis(6000)));
}
#[test]
fn timestamp_millis_now() {
let before = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
let ts = TimestampMillis::now();
let after = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
assert!(ts.0 >= before);
assert!(ts.0 <= after);
}
#[test]
fn access_sequence_ordering() {
let seq1 = AccessSequence(100);
let seq2 = AccessSequence(200);
assert!(seq1 < seq2);
assert!(seq2 > seq1);
assert_eq!(seq1, seq1);
}
#[test]
fn access_sequence_next() {
let seq = AccessSequence(5);
let next = seq.next();
assert_eq!(next.0, 6);
}
#[test]
fn access_sequence_next_saturating() {
let seq = AccessSequence(u64::MAX);
let next = seq.next();
assert_eq!(next.0, u64::MAX); }
#[test]
fn access_sequence_default() {
let seq = AccessSequence::default();
assert_eq!(seq.0, 0);
}
#[test]
fn timestamp_millis_default() {
let ts = TimestampMillis::default();
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis();
let diff = ts.0.abs_diff(now);
assert!(diff < 1000);
}
#[test]
fn timestamp_millis_serialization() {
let ts = TimestampMillis::from_millis(1234567890);
let json = serde_json::to_string(&ts).unwrap();
assert_eq!(json, "1234567890");
let deserialized: TimestampMillis = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, ts);
}
}