ratelock/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4
5//! A minimal, auditable, lock-free token bucket rate limiter.
6//!
7//! # Design goals
8//! - Zero dependencies
9//! - Zero heap allocations
10//! - No `unsafe`
11//! - `no_std` compatible core
12//! - Deterministic tests via custom [`Clock`] implementations
13//!
14//! # Behavior guarantees
15//! - [`RateLimiter::remaining`] is always bounded by `capacity`
16//! - [`RateLimiter::allow_n`] is atomic for token deduction
17//! - If time goes backwards (`now < last_refill`), refill is skipped
18//! - All public operations are panic-free
19//!
20//! # Example (`std`)
21//! ```rust
22//! # #[cfg(feature = "std")]
23//! # {
24//! use ratelock::RateLimiter;
25//!
26//! let limiter = RateLimiter::new(10, 5);
27//! assert!(limiter.allow());
28//! assert_eq!(limiter.remaining(), 9);
29//! # }
30//! ```
31//!
32//! # Example (`no_std` compatible API)
33//! ```rust
34//! use ratelock::{Clock, RateLimiter};
35//!
36//! struct FixedClock(u64);
37//! impl Clock for FixedClock {
38//! fn now_ns(&self) -> u64 {
39//! self.0
40//! }
41//! }
42//!
43//! let clock = FixedClock(0);
44//! let limiter = RateLimiter::with_clock(3, 0, clock);
45//! assert!(limiter.allow());
46//! assert!(limiter.allow());
47//! assert!(limiter.allow());
48//! assert!(!limiter.allow());
49//! ```
50
51mod clock;
52mod limiter;
53mod math;
54mod sharded;
55
56pub use clock::Clock;
57#[cfg(feature = "std")]
58pub use clock::StdClock;
59pub use limiter::{RateLimiter, Snapshot};
60pub use sharded::ShardedRateLimiter;