rate_guard/time_source/tokio_time.rs
1// rate-guard\src\time_source\tokio_time.rs
2//! Tokio time source implementation using tokio::time::Instant.
3//!
4//! This module provides TokioTimeSource, a TimeSource implementation that uses
5//! tokio::time::Instant for async-friendly operation in Tokio runtimes.
6
7#[cfg(feature = "tokio-time")]
8use tokio::time::Instant;
9use std::time::Duration;
10use crate::time_source::TimeSource;
11
12/// Tokio time source using tokio::time::Instant.
13///
14/// TokioTimeSource provides time operations that are compatible with Tokio's
15/// async runtime. It maintains an internal starting point and returns the
16/// elapsed time since that point, following the TimeSource "elapsed time" model.
17///
18/// # Characteristics
19///
20/// - **Tokio Compatible**: Uses tokio::time::Instant for async runtime compatibility
21/// - **Monotonic**: Time never goes backward
22/// - **High Precision**: Nanosecond precision from tokio::time
23/// - **Runtime Aware**: Respects Tokio's time pausing in tests
24/// - **Thread Safe**: Safe to use across multiple async tasks
25///
26/// # Examples
27///
28/// ```rust
29/// use rate_guard::time_source::{TimeSource, TokioTimeSource};
30/// use rate_guard::{Nanos, RateLimit};
31/// use rate_guard::limits::TokenBucketBuilder;
32/// use std::time::Duration;
33///
34/// #[tokio::main]
35/// async fn main() {
36/// let bucket = TokenBucketBuilder::builder()
37/// .capacity(100)
38/// .refill_amount(10)
39/// .refill_every(Duration::from_millis(100))
40/// .with_time(TokioTimeSource::new())
41/// .with_precision::<Nanos>()
42/// .build()
43/// .unwrap();
44///
45/// // Use with Tokio async runtime
46/// match bucket.try_acquire(10) {
47/// Ok(()) => println!("Request allowed"),
48/// Err(e) => println!("Rate limited: {}", e),
49/// }
50/// }
51/// ```
52#[cfg(feature = "tokio-time")]
53#[derive(Debug, Clone)]
54pub struct TokioTimeSource {
55 /// The starting instant for this time source.
56 start: Instant,
57}
58
59#[cfg(feature = "tokio-time")]
60impl TokioTimeSource {
61 /// Creates a new TokioTimeSource with the current instant as the starting point.
62 ///
63 /// The starting point is captured immediately and used as the reference
64 /// for all subsequent time measurements.
65 ///
66 /// # Examples
67 ///
68 /// ```rust
69 /// use rate_guard::time_source::TokioTimeSource;
70 ///
71 /// let time_source = TokioTimeSource::new();
72 /// // time_source.now() will return elapsed time from this moment
73 /// ```
74 #[inline(always)]
75 pub fn new() -> Self {
76 Self {
77 start: Instant::now(),
78 }
79 }
80
81 /// Returns the elapsed Duration since this time source was created.
82 ///
83 /// This is equivalent to calling the `now()` method from the TimeSource
84 /// trait, but provided as a convenience method with a more descriptive name.
85 ///
86 /// # Examples
87 ///
88 /// ```rust
89 /// use rate_guard::time_source::{TimeSource, TokioTimeSource};
90 /// use std::time::Duration;
91 ///
92 /// #[tokio::main]
93 /// async fn main() {
94 /// let time_source = TokioTimeSource::new();
95 /// tokio::time::sleep(Duration::from_millis(10)).await;
96 ///
97 /// let elapsed = time_source.elapsed();
98 /// assert!(elapsed >= Duration::from_millis(9)); // Allow tolerance
99 ///
100 /// // elapsed() and now() should return very similar values
101 /// let now_time = time_source.now();
102 /// let elapsed_time = time_source.elapsed();
103 ///
104 /// // They should be within a small tolerance
105 /// let diff = if now_time > elapsed_time {
106 /// now_time - elapsed_time
107 /// } else {
108 /// elapsed_time - now_time
109 /// };
110 /// assert!(diff < Duration::from_micros(100));
111 /// }
112 /// ```
113 #[inline(always)]
114 pub fn elapsed(&self) -> Duration {
115 self.now()
116 }
117
118 /// Returns the starting instant for this time source.
119 ///
120 /// This method provides access to the internal starting point,
121 /// which can be useful for debugging or advanced time calculations.
122 ///
123 /// # Examples
124 ///
125 /// ```rust
126 /// use rate_guard::time_source::TokioTimeSource;
127 /// use tokio::time::Instant;
128 ///
129 /// #[tokio::main]
130 /// async fn main() {
131 /// let time_source = TokioTimeSource::new();
132 /// let start_instant = time_source.start_instant();
133 ///
134 /// // The start instant should be very recent
135 /// assert!(start_instant.elapsed().as_millis() < 100);
136 /// }
137 /// ```
138 #[inline(always)]
139 pub fn start_instant(&self) -> Instant {
140 self.start
141 }
142}
143
144#[cfg(feature = "tokio-time")]
145impl Default for TokioTimeSource {
146 /// Creates a new TokioTimeSource with default settings.
147 ///
148 /// Equivalent to `TokioTimeSource::new()`.
149 #[inline(always)]
150 fn default() -> Self {
151 Self::new()
152 }
153}
154
155#[cfg(feature = "tokio-time")]
156impl TimeSource for TokioTimeSource {
157 /// Returns the elapsed Duration since this TokioTimeSource was created.
158 ///
159 /// This method returns the time elapsed since the starting instant
160 /// captured during construction. The returned Duration is guaranteed
161 /// to be monotonic and will never decrease between calls.
162 ///
163 /// # Performance
164 ///
165 /// This method is marked `#[inline(always)]` to ensure optimal performance
166 /// in high-frequency rate limiting scenarios.
167 ///
168 /// # Examples
169 ///
170 /// ```rust
171 /// use rate_guard::time_source::{TimeSource, TokioTimeSource};
172 /// use std::time::Duration;
173 ///
174 /// #[tokio::main]
175 /// async fn main() {
176 /// let time_source = TokioTimeSource::new();
177 ///
178 /// let t1 = time_source.now();
179 /// tokio::time::sleep(Duration::from_millis(10)).await;
180 /// let t2 = time_source.now();
181 ///
182 /// // Monotonic guarantee
183 /// assert!(t2 >= t1);
184 /// assert!(t2 - t1 >= Duration::from_millis(8)); // Allow tolerance
185 /// }
186 /// ```
187 #[inline(always)]
188 fn now(&self) -> Duration {
189 self.start.elapsed()
190 }
191}