Skip to main content

es_entity/clock/
handle.rs

1use chrono::{DateTime, Utc};
2
3use std::{sync::Arc, time::Duration};
4
5use super::{
6    controller::ClockController,
7    inner::ClockInner,
8    manual::ManualClock,
9    realtime::RealtimeClock,
10    sleep::{ClockSleep, ClockTimeout},
11};
12
13pub use super::sleep::Elapsed;
14
15/// A handle to a clock for getting time and performing time-based operations.
16///
17/// This is the main interface for time operations. It's cheap to clone and
18/// can be shared across tasks and threads. All clones share the same underlying
19/// clock, so they see consistent time.
20///
21/// # Creating a Clock
22///
23/// ```rust
24/// use es_entity::clock::ClockHandle;
25///
26/// // Real-time clock for production
27/// let clock = ClockHandle::realtime();
28///
29/// // Manual clock for testing - returns (handle, controller)
30/// let (clock, ctrl) = ClockHandle::manual();
31/// ```
32///
33/// # Basic Operations
34///
35/// ```rust
36/// use es_entity::clock::ClockHandle;
37/// use std::time::Duration;
38///
39/// # async fn example() {
40/// let clock = ClockHandle::realtime();
41///
42/// // Get current time
43/// let now = clock.now();
44///
45/// // Sleep for a duration
46/// clock.sleep(Duration::from_secs(1)).await;
47///
48/// // Timeout a future
49/// match clock.timeout(Duration::from_secs(5), some_async_operation()).await {
50///     Ok(result) => println!("Completed: {:?}", result),
51///     Err(_) => println!("Timed out"),
52/// }
53/// # }
54/// # async fn some_async_operation() -> i32 { 42 }
55/// ```
56#[derive(Clone)]
57pub struct ClockHandle {
58    inner: Arc<ClockInner>,
59}
60
61impl ClockHandle {
62    /// Create a real-time clock that uses the system clock and tokio timers.
63    pub fn realtime() -> Self {
64        Self {
65            inner: Arc::new(ClockInner::Realtime(RealtimeClock)),
66        }
67    }
68
69    /// Create a manual clock starting at the current time.
70    ///
71    /// Returns a tuple of `(ClockHandle, ClockController)`. The handle provides
72    /// the common time interface, while the controller provides operations
73    /// for advancing time.
74    ///
75    /// # Example
76    ///
77    /// ```rust
78    /// use es_entity::clock::ClockHandle;
79    ///
80    /// let (clock, ctrl) = ClockHandle::manual();
81    /// ```
82    pub fn manual() -> (Self, ClockController) {
83        let clock = Arc::new(ManualClock::new());
84        let handle = Self {
85            inner: Arc::new(ClockInner::Manual(Arc::clone(&clock))),
86        };
87        let controller = ClockController { clock };
88        (handle, controller)
89    }
90
91    /// Create a manual clock starting at a specific time.
92    ///
93    /// Returns a tuple of `(ClockHandle, ClockController)`. The handle provides
94    /// the common time interface, while the controller provides operations
95    /// for advancing time.
96    ///
97    /// # Example
98    ///
99    /// ```rust
100    /// use es_entity::clock::ClockHandle;
101    /// use chrono::Utc;
102    ///
103    /// let (clock, ctrl) = ClockHandle::manual_at(Utc::now() - chrono::Duration::days(30));
104    /// ```
105    pub fn manual_at(start_at: DateTime<Utc>) -> (Self, ClockController) {
106        let clock = Arc::new(ManualClock::new_at(start_at));
107        let handle = Self {
108            inner: Arc::new(ClockInner::Manual(Arc::clone(&clock))),
109        };
110        let controller = ClockController { clock };
111        (handle, controller)
112    }
113
114    /// Get the current time.
115    ///
116    /// This is a fast, synchronous operation regardless of clock type.
117    ///
118    /// For real-time clocks, this returns `Utc::now()`.
119    /// For manual clocks, this returns the current manual time.
120    #[inline]
121    pub fn now(&self) -> DateTime<Utc> {
122        match &*self.inner {
123            ClockInner::Realtime(rt) => rt.now(),
124            ClockInner::Manual(clock) => clock.now(),
125        }
126    }
127
128    /// Sleep for the given duration.
129    ///
130    /// For real-time clocks, this delegates to `tokio::time::sleep`.
131    /// For manual clocks, this waits until time is advanced.
132    pub fn sleep(&self, duration: Duration) -> ClockSleep {
133        ClockSleep::new(&self.inner, duration)
134    }
135
136    /// Sleep for the given duration with coalesceable wake-up behavior.
137    ///
138    /// Unlike [`sleep`](Self::sleep), coalesceable sleeps are processed **once**
139    /// at the end of [`advance()`](crate::ClockController::advance) rather than
140    /// at every intermediate boundary. This prevents housekeeping loops from
141    /// waking repeatedly during large time advances.
142    ///
143    /// For real-time and auto-advance clocks, this behaves identically to `sleep()`.
144    pub fn sleep_coalesce(&self, duration: Duration) -> ClockSleep {
145        ClockSleep::new_coalesceable(&self.inner, duration)
146    }
147
148    /// Apply a timeout to a future.
149    ///
150    /// Returns `Ok(output)` if the future completes before the timeout,
151    /// or `Err(Elapsed)` if the timeout expires first.
152    pub fn timeout<F>(&self, duration: Duration, future: F) -> ClockTimeout<F>
153    where
154        F: std::future::Future,
155    {
156        ClockTimeout::new(&self.inner, duration, future)
157    }
158
159    /// Check if this clock is manual (as opposed to realtime).
160    pub fn is_manual(&self) -> bool {
161        matches!(&*self.inner, ClockInner::Manual(_))
162    }
163
164    /// Get the current date (without time component).
165    ///
166    /// This is equivalent to `clock.now().date_naive()`.
167    #[inline]
168    pub fn today(&self) -> chrono::NaiveDate {
169        self.now().date_naive()
170    }
171
172    /// Get the current manual time, if this is a manual clock.
173    ///
174    /// Returns:
175    /// - `None` for realtime clocks
176    /// - `Some(time)` for manual clocks
177    ///
178    /// This is useful for code that needs to cache time when running under
179    /// manual clocks but use fresh time for realtime clocks.
180    pub fn manual_now(&self) -> Option<DateTime<Utc>> {
181        match &*self.inner {
182            ClockInner::Realtime(_) => None,
183            ClockInner::Manual(clock) => Some(clock.now()),
184        }
185    }
186}
187
188impl std::fmt::Debug for ClockHandle {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        match &*self.inner {
191            ClockInner::Realtime(_) => f.debug_struct("ClockHandle::Realtime").finish(),
192            ClockInner::Manual(clock) => f
193                .debug_struct("ClockHandle::Manual")
194                .field("now", &clock.now())
195                .finish(),
196        }
197    }
198}