Skip to main content

sim_lib_openai_server/
clock.rs

1use std::time::{SystemTime, UNIX_EPOCH};
2
3use sim_kernel::{Error, Result};
4
5/// Source of millisecond wall-clock timestamps for the gateway.
6///
7/// Abstracts time so production code can use the real system clock while tests
8/// use a reproducible deterministic clock.
9pub trait GatewayClock {
10    /// Returns the current time in milliseconds since the Unix epoch.
11    fn now_ms(&mut self) -> Result<u64>;
12}
13
14/// [`GatewayClock`] backed by the real system clock.
15#[derive(Clone, Copy, Debug, Default)]
16pub struct SystemGatewayClock;
17
18impl GatewayClock for SystemGatewayClock {
19    fn now_ms(&mut self) -> Result<u64> {
20        let duration = SystemTime::now()
21            .duration_since(UNIX_EPOCH)
22            .map_err(|err| Error::Eval(format!("system clock is before UNIX_EPOCH: {err}")))?;
23        u64::try_from(duration.as_millis())
24            .map_err(|_| Error::Eval("system timestamp exceeds u64 milliseconds".to_owned()))
25    }
26}
27
28/// [`GatewayClock`] that advances by a fixed step on each read.
29///
30/// Starts at `start_ms` and adds `step_ms` after every [`GatewayClock::now_ms`]
31/// call, producing reproducible timestamps for tests.
32#[derive(Clone, Debug)]
33pub struct DeterministicGatewayClock {
34    next_ms: u64,
35    step_ms: u64,
36}
37
38impl DeterministicGatewayClock {
39    /// Builds a clock that starts at `start_ms` and advances by `step_ms` per
40    /// read.
41    pub fn new(start_ms: u64, step_ms: u64) -> Self {
42        Self {
43            next_ms: start_ms,
44            step_ms,
45        }
46    }
47}
48
49impl GatewayClock for DeterministicGatewayClock {
50    fn now_ms(&mut self) -> Result<u64> {
51        let now = self.next_ms;
52        self.next_ms = self
53            .next_ms
54            .checked_add(self.step_ms)
55            .ok_or_else(|| Error::Eval("deterministic gateway clock overflow".to_owned()))?;
56        Ok(now)
57    }
58}