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 /// Apply a timeout to a future.
137 ///
138 /// Returns `Ok(output)` if the future completes before the timeout,
139 /// or `Err(Elapsed)` if the timeout expires first.
140 pub fn timeout<F>(&self, duration: Duration, future: F) -> ClockTimeout<F>
141 where
142 F: std::future::Future,
143 {
144 ClockTimeout::new(&self.inner, duration, future)
145 }
146
147 /// Check if this clock is manual (as opposed to realtime).
148 pub fn is_manual(&self) -> bool {
149 matches!(&*self.inner, ClockInner::Manual(_))
150 }
151
152 /// Get the current date (without time component).
153 ///
154 /// This is equivalent to `clock.now().date_naive()`.
155 #[inline]
156 pub fn today(&self) -> chrono::NaiveDate {
157 self.now().date_naive()
158 }
159
160 /// Get the current manual time, if this is a manual clock.
161 ///
162 /// Returns:
163 /// - `None` for realtime clocks
164 /// - `Some(time)` for manual clocks
165 ///
166 /// This is useful for code that needs to cache time when running under
167 /// manual clocks but use fresh time for realtime clocks.
168 pub fn manual_now(&self) -> Option<DateTime<Utc>> {
169 match &*self.inner {
170 ClockInner::Realtime(_) => None,
171 ClockInner::Manual(clock) => Some(clock.now()),
172 }
173 }
174}
175
176impl std::fmt::Debug for ClockHandle {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 match &*self.inner {
179 ClockInner::Realtime(_) => f.debug_struct("ClockHandle::Realtime").finish(),
180 ClockInner::Manual(clock) => f
181 .debug_struct("ClockHandle::Manual")
182 .field("now", &clock.now())
183 .finish(),
184 }
185 }
186}