rate_guard/time_source/mod.rs
1//! Time source abstraction for rate limiting operations.
2//!
3//! This module provides the TimeSource trait which abstracts different time
4//! sources while maintaining a clean separation of concerns. TimeSource is
5//! responsible for providing elapsed Duration since some starting point,
6//! while Precision handles the conversion to tick values.
7//!
8//! # Design Philosophy
9//!
10//! The TimeSource abstraction enables:
11//! - **Testability**: MockTimeSource for deterministic testing
12//! - **Flexibility**: Support for different time sources (std::time, tokio, etc.)
13//! - **Isolation**: Clean separation between time acquisition and precision conversion
14//! - **Performance**: Zero-cost abstractions through static dispatch
15//!
16//! # Time Model
17//!
18//! TimeSource implementations follow an "elapsed time" model similar to
19//! `std::time::Instant::elapsed()`. Each TimeSource has an implicit starting
20//! point and returns the Duration elapsed since that point.
21//!
22//! This model ensures:
23//! - Monotonic time progression
24//! - Consistent behavior across different sources
25//! - Natural integration with Duration-based APIs
26//!
27//! # Examples
28//!
29//! ## Basic Usage
30//!
31//! ```rust
32//! use rate_guard::time_source::{TimeSource, MockTimeSource};
33//! use std::time::Duration;
34//!
35//! let time_source = MockTimeSource::new();
36//! let elapsed = time_source.now();
37//! assert_eq!(elapsed, Duration::ZERO);
38//!
39//! time_source.advance(Duration::from_millis(100));
40//! let elapsed = time_source.now();
41//! assert_eq!(elapsed, Duration::from_millis(100));
42//! ```
43//!
44//! ## Integration with Precision
45//!
46//! ```rust
47//! use rate_guard::time_source::{TimeSource, MockTimeSource};
48//! use rate_guard::precision::{Precision, Millis};
49//! use std::time::Duration;
50//!
51//! let time_source = MockTimeSource::new();
52//! time_source.advance(Duration::from_millis(250));
53//!
54//! let elapsed = time_source.now();
55//! let ticks = Millis::to_ticks(elapsed);
56//! assert_eq!(ticks, 250);
57//! ```
58
59use std::time::Duration;
60
61pub mod mock;
62pub use mock::MockTimeSource;
63
64#[cfg(feature = "std-time")]
65pub mod std_time;
66#[cfg(feature = "std-time")]
67pub use std_time::StdTimeSource;
68
69#[cfg(feature = "tokio-time")]
70pub mod tokio_time;
71#[cfg(feature = "tokio-time")]
72pub use tokio_time::TokioTimeSource;
73
74/// Trait for providing elapsed time since a starting point.
75///
76/// TimeSource implementations abstract different sources of time while
77/// maintaining consistent "elapsed time" semantics. Each implementation
78/// has an implicit starting point and returns the Duration elapsed since
79/// that point.
80///
81/// # Contract
82///
83/// Implementations must guarantee:
84/// - **Monotonicity**: `now()` never decreases between calls
85/// - **Zero start**: The first call to `now()` should return `Duration::ZERO`
86/// or close to it
87/// - **Consistency**: Multiple calls without time progression return the same value
88/// - **Reasonable precision**: Should provide precision appropriate for the use case
89///
90/// # Performance Requirements
91///
92/// - Methods should be marked `#[inline(always)]` when possible
93/// - Implementations should minimize allocation and system calls
94/// - The abstraction should compile to efficient machine code
95///
96/// # Examples
97///
98/// ## Implementing a Custom TimeSource
99///
100/// ```rust
101/// use rate_guard::time_source::TimeSource;
102/// use std::time::{Duration, Instant};
103///
104/// struct CustomTimeSource {
105/// start: Instant,
106/// }
107///
108/// impl CustomTimeSource {
109/// fn new() -> Self {
110/// Self {
111/// start: Instant::now(),
112/// }
113/// }
114/// }
115///
116/// impl TimeSource for CustomTimeSource {
117/// fn now(&self) -> Duration {
118/// self.start.elapsed()
119/// }
120/// }
121/// ```
122///
123/// ## Usage in Rate Limiting
124///
125/// ```rust
126/// use rate_guard::time_source::{TimeSource, MockTimeSource};
127/// use rate_guard::precision::{Precision, Millis};
128/// use rate_guard::types::Uint;
129/// use std::time::Duration;
130///
131/// fn get_current_ticks<T: TimeSource, P: Precision>(time_source: &T) -> Uint {
132/// let elapsed = time_source.now();
133/// P::to_ticks(elapsed)
134/// }
135///
136/// let time_source = MockTimeSource::new();
137/// time_source.advance(Duration::from_millis(500));
138///
139/// let ticks = get_current_ticks::<_, Millis>(&time_source);
140/// assert_eq!(ticks, 500);
141/// ```
142pub trait TimeSource {
143 /// Returns the elapsed Duration since this TimeSource's starting point.
144 ///
145 /// This method should return a monotonically increasing Duration that
146 /// represents the time elapsed since some fixed starting point. The
147 /// starting point is implementation-defined but should remain consistent
148 /// for the lifetime of the TimeSource instance.
149 ///
150 /// # Returns
151 ///
152 /// A Duration representing elapsed time since the starting point.
153 /// The first call should return `Duration::ZERO` or very close to it.
154 ///
155 /// # Guarantees
156 ///
157 /// - **Monotonic**: Each call returns a value >= the previous call
158 /// - **Consistent**: Multiple calls without time progression return the same value
159 /// - **Bounded**: The returned Duration should not exceed reasonable limits
160 ///
161 /// # Examples
162 ///
163 /// ```rust
164 /// use rate_guard::time_source::{TimeSource, MockTimeSource};
165 /// use std::time::Duration;
166 ///
167 /// let time_source = MockTimeSource::new();
168 ///
169 /// let t1 = time_source.now();
170 /// assert_eq!(t1, Duration::ZERO);
171 ///
172 /// time_source.advance(Duration::from_millis(100));
173 /// let t2 = time_source.now();
174 /// assert_eq!(t2, Duration::from_millis(100));
175 ///
176 /// // Monotonicity guarantee
177 /// assert!(t2 >= t1);
178 /// ```
179 fn now(&self) -> Duration;
180}