rate_guard/time_source/
std_time.rs

1//! Standard library time source implementation using std::time::Instant.
2//!
3//! This module provides StdTimeSource, a TimeSource implementation that uses
4//! std::time::Instant for real-time operation. It follows the "elapsed time"
5//! model where time is measured relative to when the TimeSource was created.
6//!
7//! # Features
8//!
9//! - **Real Time**: Uses actual system time via std::time::Instant
10//! - **Monotonic**: Guaranteed monotonic time progression
11//! - **High Precision**: Nanosecond precision from std::time
12//! - **Cross Platform**: Works on all platforms supported by std::time
13//!
14//! # Usage
15//!
16//! This implementation is only available when the `std-time` feature is enabled.
17//! It's designed for production use where real system time is required.
18//!
19//! # Examples
20//!
21//! ## Basic Usage
22//!
23//! ```rust
24//! use rate_guard::time_source::{TimeSource, StdTimeSource};
25//! use std::time::Duration;
26//!
27//! let time_source = StdTimeSource::new();
28//! let start = time_source.now();
29//! 
30//! // Some time passes...
31//! std::thread::sleep(Duration::from_millis(100));
32//! 
33//! let elapsed = time_source.now();
34//! assert!(elapsed >= start + Duration::from_millis(90)); // Allow some tolerance
35//! ```
36//!
37//! ## Integration with Rate Limiters
38//!
39//! ```rust
40//! use rate_guard::{Nanos, StdTimeSource, RateLimit};
41//! use rate_guard::limits::TokenBucketBuilder;
42//! use std::time::Duration;
43//!
44//! let bucket = TokenBucketBuilder::builder()
45//!     .capacity(100)
46//!     .refill_amount(10)
47//!     .refill_every(Duration::from_millis(100))
48//!     .with_time(StdTimeSource::new())
49//!     .with_precision::<Nanos>()
50//!     .build()
51//!     .unwrap();
52//!
53//! // Use with real system time
54//! match bucket.try_acquire(10) {
55//!     Ok(()) => println!("Request allowed"),
56//!     Err(e) => println!("Rate limited: {}", e),
57//! }
58//! ```
59
60use std::time::{Duration, Instant};
61use crate::time_source::TimeSource;
62
63/// Standard library time source using std::time::Instant.
64///
65/// StdTimeSource provides real-time operation using std::time::Instant.
66/// It maintains an internal starting point and returns the elapsed time
67/// since that point, following the TimeSource "elapsed time" model.
68///
69/// # Characteristics
70///
71/// - **Monotonic**: Time never goes backward
72/// - **High Precision**: Nanosecond precision from std::time
73/// - **Real Time**: Uses actual system time
74/// - **Thread Safe**: Safe to use across multiple threads
75///
76/// # Time Model
77///
78/// The time source captures a starting Instant when created and returns
79/// the elapsed Duration since that point. This ensures:
80/// - Consistent zero point across the lifetime of the time source
81/// - Monotonic progression even across system clock adjustments
82/// - Predictable behavior for rate limiting calculations
83///
84/// # Examples
85///
86/// ## Creating and Using
87///
88/// ```rust
89/// use rate_guard::time_source::{TimeSource, StdTimeSource};
90/// use std::time::Duration;
91///
92/// let time_source = StdTimeSource::new();
93/// 
94/// // Initially close to zero
95/// let start = time_source.now();
96/// assert!(start < Duration::from_millis(1));
97///
98/// // Time progresses naturally
99/// std::thread::sleep(Duration::from_millis(50));
100/// let later = time_source.now();
101/// assert!(later >= Duration::from_millis(45)); // Allow tolerance
102/// ```
103///
104/// ## Precision Demonstration
105///
106/// ```rust
107/// use rate_guard::time_source::{TimeSource, StdTimeSource};
108/// use rate_guard::precision::{Precision, Nanos, Millis};
109///
110/// let time_source = StdTimeSource::new();
111/// std::thread::sleep(std::time::Duration::from_millis(10));
112/// 
113/// let elapsed = time_source.now();
114/// let nanos = Nanos::to_ticks(elapsed);
115/// let millis = Millis::to_ticks(elapsed);
116/// 
117/// println!("Elapsed: {} ns, {} ms", nanos, millis);
118/// ```
119#[derive(Debug, Clone)]
120pub struct StdTimeSource {
121    /// The starting instant for this time source.
122    start: Instant,
123}
124
125impl StdTimeSource {
126    /// Creates a new StdTimeSource with the current instant as the starting point.
127    ///
128    /// The starting point is captured immediately and used as the reference
129    /// for all subsequent time measurements.
130    ///
131    /// # Examples
132    ///
133    /// ```rust
134    /// use rate_guard::time_source::StdTimeSource;
135    ///
136    /// let time_source = StdTimeSource::new();
137    /// // time_source.now() will return elapsed time from this moment
138    /// ```
139    #[inline(always)]
140    pub fn new() -> Self {
141        Self {
142            start: Instant::now(),
143        }
144    }
145
146    /// Returns the elapsed Duration since this time source was created.
147    ///
148    /// This is equivalent to calling the `now()` method from the TimeSource
149    /// trait, but provided as a convenience method with a more descriptive name.
150    ///
151    /// # Examples
152    ///
153    /// ```rust
154    /// use rate_guard::time_source::{TimeSource, StdTimeSource};
155    /// use std::time::Duration;
156    ///
157    /// let time_source = StdTimeSource::new();
158    /// std::thread::sleep(Duration::from_millis(10));
159    /// 
160    /// let elapsed = time_source.elapsed();
161    /// assert!(elapsed >= Duration::from_millis(9)); // Allow tolerance
162    /// 
163    /// // elapsed() and now() should return very similar values
164    /// let now_time = time_source.now();
165    /// let elapsed_time = time_source.elapsed();
166    /// 
167    /// // They should be within a small tolerance (microseconds)
168    /// let diff = if now_time > elapsed_time {
169    ///     now_time - elapsed_time
170    /// } else {
171    ///     elapsed_time - now_time
172    /// };
173    /// assert!(diff < Duration::from_micros(100)); // Should be very close
174    /// ```
175    #[inline(always)]
176    pub fn elapsed(&self) -> Duration {
177        self.now()
178    }
179
180    /// Returns the starting instant for this time source.
181    ///
182    /// This method provides access to the internal starting point,
183    /// which can be useful for debugging or advanced time calculations.
184    ///
185    /// # Examples
186    ///
187    /// ```rust
188    /// use rate_guard::time_source::StdTimeSource;
189    /// use std::time::Instant;
190    ///
191    /// let time_source = StdTimeSource::new();
192    /// let start_instant = time_source.start_instant();
193    /// 
194    /// // The start instant should be very recent
195    /// assert!(start_instant.elapsed().as_millis() < 100);
196    /// ```
197    #[inline(always)]
198    pub fn start_instant(&self) -> Instant {
199        self.start
200    }
201}
202
203impl Default for StdTimeSource {
204    /// Creates a new StdTimeSource with default settings.
205    ///
206    /// Equivalent to `StdTimeSource::new()`.
207    #[inline(always)]
208    fn default() -> Self {
209        Self::new()
210    }
211}
212
213impl TimeSource for StdTimeSource {
214    /// Returns the elapsed Duration since this StdTimeSource was created.
215    ///
216    /// This method returns the time elapsed since the starting instant
217    /// captured during construction. The returned Duration is guaranteed
218    /// to be monotonic and will never decrease between calls.
219    ///
220    /// # Performance
221    ///
222    /// This method is marked `#[inline(always)]` to ensure optimal performance
223    /// in high-frequency rate limiting scenarios.
224    ///
225    /// # Examples
226    ///
227    /// ```rust
228    /// use rate_guard::time_source::{TimeSource, StdTimeSource};
229    /// use std::time::Duration;
230    ///
231    /// let time_source = StdTimeSource::new();
232    ///
233    /// let t1 = time_source.now();
234    /// std::thread::sleep(Duration::from_millis(10));
235    /// let t2 = time_source.now();
236    ///
237    /// // Monotonic guarantee
238    /// assert!(t2 >= t1);
239    /// assert!(t2 - t1 >= Duration::from_millis(8)); // Allow tolerance
240    /// ```
241    #[inline(always)]
242    fn now(&self) -> Duration {
243        self.start.elapsed()
244    }
245}