pub trait Clock {
fn delta(&mut self) -> f32;
}
#[derive(Debug)]
pub struct WallClock {
last: std::time::Instant,
}
impl WallClock {
pub fn new() -> Self {
Self {
last: std::time::Instant::now(),
}
}
}
impl Default for WallClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for WallClock {
fn delta(&mut self) -> f32 {
let now = std::time::Instant::now();
let dt = now.duration_since(self.last).as_secs_f32();
self.last = now;
dt
}
}
#[derive(Debug, Default)]
pub struct ManualClock {
pending: f32,
}
impl ManualClock {
pub fn new() -> Self {
Self { pending: 0.0 }
}
pub fn advance(&mut self, dt: f32) {
self.pending = dt.max(0.0);
}
}
impl Clock for ManualClock {
fn delta(&mut self) -> f32 {
let dt = self.pending;
self.pending = 0.0;
dt
}
}
#[derive(Debug, Clone)]
pub struct MockClock {
step: f32,
}
impl MockClock {
pub fn new(step_seconds: f32) -> Self {
Self {
step: step_seconds.max(0.0),
}
}
}
impl Clock for MockClock {
fn delta(&mut self) -> f32 {
self.step
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mock_clock_always_returns_step() {
let mut clk = MockClock::new(1.0 / 60.0);
for _ in 0..10 {
let dt = clk.delta();
assert!((dt - 1.0 / 60.0).abs() < 1e-6);
}
}
#[test]
fn manual_clock_advance_and_consume() {
let mut clk = ManualClock::new();
clk.advance(0.016);
assert!((clk.delta() - 0.016).abs() < 1e-6);
assert_eq!(clk.delta(), 0.0);
}
#[test]
fn manual_clock_negative_clamped() {
let mut clk = ManualClock::new();
clk.advance(-5.0);
assert_eq!(clk.delta(), 0.0);
}
#[test]
fn wall_clock_non_negative() {
let mut clk = WallClock::new();
let dt = clk.delta();
assert!(dt >= 0.0);
}
#[test]
fn mock_clock_zero_step() {
let mut clk = MockClock::new(0.0);
assert_eq!(clk.delta(), 0.0);
}
}