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}