rate-guard 0.1.0

Thread-safe rate limiting library with multiple algorithms and Duration-based configuration
Documentation
//! Mock time source implementation for deterministic testing.
//!
//! This module provides MockTimeSource, a controllable time source that allows
//! manual manipulation of time for testing rate limiting behavior. It maintains
//! the elapsed time model while providing full control over time progression.
//!
//! # Design Features
//!
//! - **Deterministic**: Time only advances when explicitly requested
//! - **Monotonic**: Time never goes backward, even with manual control
//! - **Thread-local**: Each thread has independent mock time state
//! - **Simple API**: Easy to use in tests and examples
//!
//! # Examples
//!
//! ## Basic Time Control
//!
//! ```rust
//! use rate_guard::time_source::{TimeSource, MockTimeSource};
//! use std::time::Duration;
//!
//! let time_source = MockTimeSource::new();
//!
//! // Initially at zero
//! assert_eq!(time_source.now(), Duration::ZERO);
//!
//! // Advance time manually
//! time_source.advance(Duration::from_millis(100));
//! assert_eq!(time_source.now(), Duration::from_millis(100));
//!
//! // Set absolute time
//! time_source.set_elapsed(Duration::from_secs(5));
//! assert_eq!(time_source.now(), Duration::from_secs(5));
//! ```
//!
//! ## Rate Limiter Testing
//!
//! ```rust
//! use rate_guard::time_source::{TimeSource, MockTimeSource};
//! use rate_guard::precision::{Precision, Millis};
//! use std::time::Duration;
//!
//! let time_source = MockTimeSource::new();
//!
//! // Simulate rate limiter behavior
//! let initial_ticks = Millis::to_ticks(time_source.now());
//! assert_eq!(initial_ticks, 0);
//!
//! // Advance time to trigger refill
//! time_source.advance(Duration::from_millis(100));
//! let later_ticks = Millis::to_ticks(time_source.now());
//! assert_eq!(later_ticks, 100);
//! ```

use std::time::Duration;
use std::cell::Cell;
use crate::time_source::TimeSource;

thread_local! {
    /// Thread-local mock time state.
    ///
    /// This thread-local variable holds the current elapsed time value that can
    /// be controlled in tests. Each thread has its own independent mock time,
    /// ensuring test isolation and avoiding synchronization overhead.
    static MOCK_ELAPSED: Cell<Duration> = Cell::new(Duration::ZERO);
}

/// Mock time source for deterministic testing.
///
/// MockTimeSource provides a controllable time source where elapsed time can
/// be manually set and advanced. This enables deterministic testing of rate
/// limiting behavior without depending on real system time.
///
/// # Time Model
///
/// MockTimeSource maintains an elapsed Duration since an implicit starting point.
/// Time starts at `Duration::ZERO` and can be advanced or set to specific values.
/// Time is always monotonic - attempting to set time backward will be ignored.
///
/// # Thread Safety
///
/// MockTimeSource uses thread-local storage, so each thread has its own
/// independent mock time. This is perfect for testing scenarios where you
/// want isolated time control per test.
///
/// # Examples
///
/// ## Basic Usage
///
/// ```rust
/// use rate_guard::time_source::{TimeSource, MockTimeSource};
/// use std::time::Duration;
///
/// let time_source = MockTimeSource::new();
///
/// // Check initial state
/// assert_eq!(time_source.now(), Duration::ZERO);
///
/// // Advance time
/// time_source.advance(Duration::from_millis(50));
/// assert_eq!(time_source.now(), Duration::from_millis(50));
///
/// // Advance more
/// time_source.advance(Duration::from_millis(30));
/// assert_eq!(time_source.now(), Duration::from_millis(80));
/// ```
///
/// ## Absolute Time Setting
///
/// ```rust
/// use rate_guard::time_source::{TimeSource, MockTimeSource};
/// use std::time::Duration;
///
/// let time_source = MockTimeSource::new();
///
/// // Jump to specific time
/// time_source.set_elapsed(Duration::from_secs(10));
/// assert_eq!(time_source.now(), Duration::from_secs(10));
///
/// // Reset to zero
/// time_source.reset();
/// assert_eq!(time_source.now(), Duration::ZERO);
/// ```
///
/// ## Rate Limiter Integration
///
/// ```rust
/// use rate_guard::time_source::{TimeSource, MockTimeSource};
/// use std::time::Duration;
///
/// let time_source = MockTimeSource::new();
///
/// // Simulate token bucket refill timing
/// let refill_interval = Duration::from_millis(100);
/// let initial_time = time_source.now();
///
/// // Advance past refill interval
/// time_source.advance(refill_interval);
/// let elapsed = time_source.now() - initial_time;
/// assert!(elapsed >= refill_interval);
/// ```
#[derive(Debug, Clone)]
pub struct MockTimeSource;

impl MockTimeSource {
    /// Creates a new MockTimeSource.
    ///
    /// The time source starts with elapsed time of `Duration::ZERO`.
    /// Each instance shares the same thread-local time state within
    /// the current thread.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rate_guard::time_source::{TimeSource, MockTimeSource};
    /// use std::time::Duration;
    ///
    /// let time_source = MockTimeSource::new();
    /// assert_eq!(time_source.now(), Duration::ZERO);
    /// ```
    pub fn new() -> Self {
        Self
    }

    /// Sets the elapsed time to a specific value.
    ///
    /// This method allows tests to jump to any point in time. The time
    /// must be greater than or equal to the current elapsed time to
    /// maintain monotonicity. Attempts to set time backward are ignored.
    ///
    /// # Arguments
    /// * `elapsed` - The new elapsed time value
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rate_guard::time_source::{TimeSource, MockTimeSource};
    /// use std::time::Duration;
    ///
    /// let time_source = MockTimeSource::new();
    ///
    /// time_source.set_elapsed(Duration::from_millis(1000));
    /// assert_eq!(time_source.now(), Duration::from_millis(1000));
    ///
    /// // Attempting to go backward is ignored
    /// time_source.set_elapsed(Duration::from_millis(500));
    /// assert_eq!(time_source.now(), Duration::from_millis(1000));
    /// ```
    pub fn set_elapsed(&self, elapsed: Duration) {
        MOCK_ELAPSED.with(|cell| {
            let current = cell.get();
            if elapsed >= current {
                cell.set(elapsed);
            }
        });
    }

    /// Advances the elapsed time by the specified duration.
    ///
    /// This method adds the specified duration to the current elapsed time,
    /// simulating the passage of time in tests. The advancement is always
    /// forward, maintaining monotonicity.
    ///
    /// # Arguments
    /// * `duration` - Duration to advance the time by
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rate_guard::time_source::{TimeSource, MockTimeSource};
    /// use std::time::Duration;
    ///
    /// let time_source = MockTimeSource::new();
    ///
    /// time_source.advance(Duration::from_millis(50));
    /// assert_eq!(time_source.now(), Duration::from_millis(50));
    ///
    /// time_source.advance(Duration::from_millis(25));
    /// assert_eq!(time_source.now(), Duration::from_millis(75));
    /// ```
    pub fn advance(&self, duration: Duration) {
        MOCK_ELAPSED.with(|cell| {
            let current = cell.get();
            cell.set(current + duration);
        });
    }

    /// Resets the elapsed time to zero.
    ///
    /// This is a convenience method equivalent to `set_elapsed(Duration::ZERO)`.
    /// Useful for resetting time state between tests.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rate_guard::time_source::{TimeSource, MockTimeSource};
    /// use std::time::Duration;
    ///
    /// let time_source = MockTimeSource::new();
    /// time_source.advance(Duration::from_secs(5));
    /// assert_eq!(time_source.now(), Duration::from_secs(5));
    ///
    /// time_source.reset();
    /// assert_eq!(time_source.now(), Duration::ZERO);
    /// ```
    pub fn reset(&self) {
        MOCK_ELAPSED.with(|cell| {
            cell.set(Duration::ZERO);
        });
    }

    /// Returns the current elapsed time.
    ///
    /// This is equivalent to calling the `now()` method from the TimeSource
    /// trait, but provided as a convenience method with a more descriptive name.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rate_guard::time_source::MockTimeSource;
    /// use std::time::Duration;
    ///
    /// let time_source = MockTimeSource::new();
    /// time_source.advance(Duration::from_millis(100));
    ///
    /// assert_eq!(time_source.elapsed(), Duration::from_millis(100));
    /// assert_eq!(time_source.elapsed(), time_source.now());
    /// ```
    pub fn elapsed(&self) -> Duration {
        self.now()
    }

    pub fn now(&self) -> Duration {
        MOCK_ELAPSED.with(|cell| cell.get())
    }
}

impl Default for MockTimeSource {
    /// Creates a new MockTimeSource with default settings.
    ///
    /// Equivalent to `MockTimeSource::new()`.
    fn default() -> Self {
        Self::new()
    }
}

impl TimeSource for MockTimeSource {
    /// Returns the current elapsed time since the mock starting point.
    ///
    /// This method returns the elapsed Duration that has been set or advanced
    /// through the mock time control methods.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use rate_guard::time_source::{TimeSource, MockTimeSource};
    /// use std::time::Duration;
    ///
    /// let time_source = MockTimeSource::new();
    /// assert_eq!(time_source.now(), Duration::ZERO);
    ///
    /// time_source.advance(Duration::from_millis(123));
    /// assert_eq!(time_source.now(), Duration::from_millis(123));
    /// ```
    #[inline(always)]
    fn now(&self) -> Duration {
        MOCK_ELAPSED.with(|cell| cell.get())
    }
}