1use std::sync::Mutex;
4
5use jiff::{SignedDuration, Timestamp};
6
7pub fn timestamp_from_micros(value: i64) -> Result<Timestamp, crate::StoreError> {
12 Timestamp::new(
13 value.div_euclid(1_000_000),
14 (value.rem_euclid(1_000_000) * 1_000) as i32,
15 )
16 .map_err(|_| crate::StoreError("stored microseconds exceed the timestamp domain".into()))
17}
18
19pub trait Clock: Send + Sync + 'static {
23 fn now(&self) -> Timestamp;
25}
26
27#[derive(Debug, Default, Clone, Copy)]
29pub struct SystemClock;
30
31impl Clock for SystemClock {
32 fn now(&self) -> Timestamp {
33 #[allow(
34 clippy::disallowed_methods,
35 reason = "this is the one production read of the business clock; every other caller takes a Timestamp from a Clock, which is what makes the rest of the system replayable"
36 )]
37 Timestamp::now()
38 }
39}
40
41#[derive(Debug)]
43pub struct ManualClock(Mutex<Timestamp>);
44
45impl ManualClock {
46 #[must_use]
48 pub fn new(start: Timestamp) -> Self {
49 ManualClock(Mutex::new(start))
50 }
51
52 pub fn set(&self, to: Timestamp) {
59 *self.0.lock().expect("manual clock poisoned") = to;
60 }
61
62 pub fn advance(&self, by: SignedDuration) {
69 let mut guard = self.0.lock().expect("manual clock poisoned");
70 *guard = guard.checked_add(by).expect("manual clock overflow");
71 }
72}
73
74impl Clock for ManualClock {
75 fn now(&self) -> Timestamp {
76 *self.0.lock().expect("manual clock poisoned")
77 }
78}