moonpool_sim/providers/
time.rs

1//! Simulation time provider implementation.
2
3use async_trait::async_trait;
4use std::time::Duration;
5
6use moonpool_core::{SimulationResult, TimeProvider};
7
8use crate::sim::WeakSimWorld;
9
10/// Simulation time provider that integrates with SimWorld.
11#[derive(Debug, Clone)]
12pub struct SimTimeProvider {
13    sim: WeakSimWorld,
14}
15
16impl SimTimeProvider {
17    /// Create a new simulation time provider.
18    pub fn new(sim: WeakSimWorld) -> Self {
19        Self { sim }
20    }
21}
22
23#[async_trait(?Send)]
24impl TimeProvider for SimTimeProvider {
25    async fn sleep(&self, duration: Duration) -> SimulationResult<()> {
26        let sleep_future = self.sim.sleep(duration)?;
27        let _ = sleep_future.await;
28        Ok(())
29    }
30
31    fn now(&self) -> Duration {
32        // Return exact simulation time (equivalent to FDB's now())
33        self.sim.now().unwrap_or(Duration::ZERO)
34    }
35
36    fn timer(&self) -> Duration {
37        // Return drifted timer time (equivalent to FDB's timer())
38        // Can be up to clock_drift_max ahead of now()
39        self.sim.timer().unwrap_or(Duration::ZERO)
40    }
41
42    async fn timeout<F, T>(&self, duration: Duration, future: F) -> SimulationResult<Result<T, ()>>
43    where
44        F: std::future::Future<Output = T>,
45    {
46        let sleep_future = self.sim.sleep(duration)?;
47
48        // Race the future against the timeout using tokio::select!
49        // Both futures respect simulation time through the event system
50        tokio::select! {
51            result = future => Ok(Ok(result)),
52            _ = sleep_future => Ok(Err(())),
53        }
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::sim::SimWorld;
61
62    #[tokio::test]
63    async fn test_sim_time_provider_basic() {
64        let sim = SimWorld::new();
65        let time_provider = SimTimeProvider::new(sim.downgrade());
66
67        // Test now - should return simulation time (starts at zero)
68        let now = time_provider.now();
69        assert_eq!(now, Duration::ZERO);
70
71        // Test timer - should be >= now
72        let timer = time_provider.timer();
73        assert!(timer >= now);
74
75        // Test timeout - quick completion (no simulation advancement needed)
76        let result = time_provider
77            .timeout(Duration::from_millis(100), async { 42 })
78            .await;
79        assert!(result.is_ok());
80        assert_eq!(result.expect("timeout should complete"), Ok(42));
81    }
82
83    #[test]
84    fn test_sim_time_provider_with_simulation() {
85        let sim = SimWorld::new();
86        let time_provider = SimTimeProvider::new(sim.downgrade());
87
88        // Test now - should return simulation time (starts at zero)
89        let now = time_provider.now();
90        assert_eq!(now, Duration::ZERO);
91
92        // Test timer - should be >= now but <= now + clock_drift_max
93        let timer = time_provider.timer();
94        assert!(timer >= now);
95        // Default clock_drift_max is 100ms
96        assert!(timer <= now + Duration::from_millis(100));
97    }
98
99    #[test]
100    fn test_clock_drift_bounds() {
101        let sim = SimWorld::new();
102        let time_provider = SimTimeProvider::new(sim.downgrade());
103
104        // Call timer multiple times to exercise drift logic
105        for _ in 0..100 {
106            let now = time_provider.now();
107            let timer = time_provider.timer();
108
109            // timer should always be >= now
110            assert!(timer >= now, "timer ({:?}) < now ({:?})", timer, now);
111
112            // timer should not exceed now + clock_drift_max (100ms default)
113            assert!(
114                timer <= now + Duration::from_millis(100),
115                "timer ({:?}) > now + 100ms ({:?})",
116                timer,
117                now + Duration::from_millis(100)
118            );
119        }
120    }
121}