rate_guard_core/
lib.rs

1//! A comprehensive rate limiting library for Rust applications.
2//!
3//! This library provides multiple rate limiting algorithms with a focus on performance,
4//! accuracy, and ease of use. All implementations are thread-safe and designed for
5//! high-concurrency scenarios.
6//!
7//! Time is represented using abstract "ticks" — unit-less integers that typically map
8//! to nanoseconds, but can represent any monotonic unit you choose.
9//!
10//! # Quick Start
11//!
12//! ```rust
13//! use rate_guard_core::rate_limiters::TokenBucketCore;
14//!
15//! // Capacity: 100 tokens
16//! // Refill: 10 tokens every 5 ticks
17//! let limiter = TokenBucketCore::new(100, 5, 10);
18//!
19//! // Try to acquire 20 tokens at tick 0
20//! match limiter.try_acquire_at(20, 0) {
21//!     Ok(()) => println!("Request allowed"),
22//!     Err(e) => println!("Request denied: {}", e),
23//! }
24//! ```
25//!
26//! # Available Rate Limiting Algorithms
27//!
28//! ## [Leaky Bucket](rate_limiters::LeakyBucketCore)
29//! Tokens leak out at a constant rate, providing smooth traffic shaping:
30//!
31//! ```rust
32//! # use rate_guard_core::rate_limiters::LeakyBucketCore;
33//! let limiter = LeakyBucketCore::new(100, 10, 5); // leak 5 tokens every 10 ticks
34//! ```
35//!
36//! ## [Token Bucket](rate_limiters::TokenBucketCore)
37//! Allows bursts up to capacity while maintaining average rate:
38//!
39//! ```rust
40//! # use rate_guard_core::rate_limiters::TokenBucketCore;
41//! let limiter = TokenBucketCore::new(100, 10, 5); // add 5 tokens every 10 ticks
42//! ```
43//!
44//! ## [Fixed Window Counter](rate_limiters::FixedWindowCounterCore)
45//! Simple time-window based counting:
46//!
47//! ```rust
48//! # use rate_guard_core::rate_limiters::FixedWindowCounterCore;
49//! let limiter = FixedWindowCounterCore::new(100, 60); // 100 requests per 60 ticks
50//! ```
51//!
52//! ## [Sliding Window Counter](rate_limiters::SlidingWindowCounterCore)
53//! Accurate sliding window using multiple time buckets:
54//!
55//! ```rust
56//! # use rate_guard_core::rate_limiters::SlidingWindowCounterCore;
57//! let limiter = SlidingWindowCounterCore::new(100, 10, 6); // 100 requests per 60 ticks
58//! ```
59//!
60//! ## [Approximate Sliding Window](rate_limiters::ApproximateSlidingWindowCore)
61//! Memory-efficient approximation using only two windows:
62//!
63//! ```rust
64//! # use rate_guard_core::rate_limiters::ApproximateSlidingWindowCore;
65//! let limiter = ApproximateSlidingWindowCore::new(100, 60); // ~100 requests per 60 ticks
66//! ```
67//!
68//! # Core Concepts
69//!
70//! ## Time Representation
71//! All algorithms use abstract "ticks" to represent time. You can map ticks to any unit
72//! (e.g., milliseconds, nanoseconds). Internally, `Tick` is an unsigned integer (`u64` or `u128`)
73//! based on crate features.
74//!
75//! ## Error Handling
76//! All rate limiters return [`AcquireResult`] which can indicate:
77//! - **Success** — Request was allowed
78//! - **[`ExceedsCapacity`](RateLimitError::ExceedsCapacity)** — Rate limit exceeded
79//! - **[`ContentionFailure`](RateLimitError::ContentionFailure)** — Lock contention
80//! - **[`ExpiredTick`](RateLimitError::ExpiredTick)** — Time went backwards or was reused
81//!
82//! ## Thread Safety
83//! All rate limiters are thread-safe and use non-blocking locks. If a lock cannot
84//! be acquired immediately, `ContentionFailure` is returned rather than blocking.
85//!
86//! # Feature Flags
87//!
88//! This crate supports selecting the internal tick precision:
89//!
90//! - `tick_u64` *(default)* — `Tick = u64`, supports ~584 years of nanosecond ticks
91//! - `tick_u128` — `Tick = u128`, supports extremely long durations or ultra-high precision
92//!
93//! To use `u128`, compile with:
94//! ```sh
95//! cargo build --no-default-features --features tick_u128
96//! ```
97
98pub mod types;
99pub mod rate_limiters;
100pub mod rate_limiter_core;
101pub use types::Uint;
102
103/// Error types for rate limiter operations.
104///
105/// These errors indicate different failure modes when attempting to acquire
106/// tokens from a rate limiter.
107#[derive(Debug, PartialEq, Eq, Clone, Copy)]
108pub enum RateLimitError {
109    /// Request exceeds available capacity.
110    ///
111    /// This indicates that allowing the request would violate the rate limit.
112    /// The caller should either:
113    /// - Reject the request
114    /// - Wait and retry later
115    /// - Reduce the number of tokens requested
116    ExceedsCapacity,
117
118    /// Failed due to contention with other threads.
119    ///
120    /// This occurs when the internal lock cannot be acquired immediately.
121    /// The caller should typically:
122    /// - Retry the operation
123    /// - Implement backoff strategy
124    /// - Consider the request as temporarily failed
125    ContentionFailure,
126
127    /// The provided tick is too old or regressed.
128    ///
129    /// This occurs when:
130    /// - Time appears to go backwards
131    /// - An older tick is reused
132    /// - System clock adjustments occur
133    ExpiredTick,
134}
135
136impl std::fmt::Display for RateLimitError {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        match self {
139            RateLimitError::ExceedsCapacity => write!(f, "Request exceeds available capacity"),
140            RateLimitError::ContentionFailure => write!(f, "Failed due to thread contention"),
141            RateLimitError::ExpiredTick => write!(f, "The provided tick is too old or regressed"),
142        }
143    }
144}
145
146impl std::error::Error for RateLimitError {}
147
148/// Result type for acquire operations.
149///
150/// Convenience alias for operations that either succeed (`()`) or fail
151/// with a [`RateLimitError`].
152///
153/// # Example
154/// ```rust
155/// use rate_guard_core::{AcquireResult, RateLimitError};
156/// use rate_guard_core::rate_limiters::TokenBucketCore;
157///
158/// let limiter = TokenBucketCore::new(10, 1, 1);
159/// let result: AcquireResult = limiter.try_acquire_at(5, 0);
160///
161/// match result {
162///     Ok(()) => println!("Acquired 5 tokens"),
163///     Err(RateLimitError::ExceedsCapacity) => println!("Not enough tokens"),
164///     Err(e) => println!("Other error: {}", e),
165/// }
166/// ```
167pub type AcquireResult = Result<(), RateLimitError>;