use core::fmt;
use core::ops::Add;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use serde::{Deserialize, Serialize};
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[serde(transparent)]
pub struct Millis(pub u64);
impl Millis {
pub const ZERO: Millis = Millis(0);
pub const fn since(self, earlier: Millis) -> u64 {
self.0.saturating_sub(earlier.0)
}
}
impl Add<u64> for Millis {
type Output = Millis;
fn add(self, ms: u64) -> Millis {
Millis(self.0.saturating_add(ms))
}
}
impl fmt::Display for Millis {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}ms", self.0)
}
}
pub trait Clock: Send + Sync {
fn now(&self) -> Millis;
}
impl<F> Clock for F
where
F: Fn() -> Millis + Send + Sync,
{
fn now(&self) -> Millis {
self()
}
}
#[derive(Clone, Debug, Default)]
pub struct FakeClock(Arc<AtomicU64>);
impl FakeClock {
pub fn new() -> Self {
Self::default()
}
pub fn at(start: Millis) -> Self {
Self(Arc::new(AtomicU64::new(start.0)))
}
pub fn set(&self, now: Millis) {
self.0.store(now.0, Ordering::SeqCst);
}
pub fn advance(&self, ms: u64) {
let mut current = self.0.load(Ordering::SeqCst);
loop {
let next = current.saturating_add(ms);
match self
.0
.compare_exchange(current, next, Ordering::SeqCst, Ordering::SeqCst)
{
Ok(_) => return,
Err(actual) => current = actual,
}
}
}
}
impl Clock for FakeClock {
fn now(&self) -> Millis {
Millis(self.0.load(Ordering::SeqCst))
}
}