# Rate-Guard
A flexible and type-safe rate limiting library for Rust.
[](https://crates.io/crates/rate-guard)
[](https://docs.rs/rate-guard)
[](https://opensource.org/licenses/MIT)
[](https://opensource.org/licenses/Apache-2.0)
Rate-Guard provides a unified interface for multiple rate limiting algorithms with Duration-based configuration, type-safe time handling, and comprehensive error reporting. It's designed to integrate seamlessly with Rust's async ecosystem while maintaining high performance through zero-cost abstractions.
## Features
- **Duration-Based Configuration**: All timing parameters use `std::time::Duration`
- **Multiple Algorithms**: token bucket, fixed window counter, sliding window counter, and approximate sliding window
- **Time Source Abstraction**: Pluggable time sources (mock, std::time, etc.)
- **Precision Control**: Compile-time precision selection for optimal performance
- **Builder Pattern**: Fluent configuration API with compile-time validation
- **Zero-Cost Abstractions**: High-level ergonomics without runtime overhead
- **Comprehensive Testing**: Built-in mock time source for deterministic testing
---
## Quick Start
Add this to your `Cargo.toml`:
```toml
[dependencies]
rate-guard = "0.1.0"
# Optional: Enable std::time support
rate-guard = { version = "0.1.0", features = ["std-time"] }
```
Basic usage:
```rust
use rate_guard::{Nanos, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;
// Create a token bucket that allows 100 requests with 10 refills every 100ms
let bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build()
.unwrap();
// Check if request is allowed
match bucket.try_acquire(1) {
Ok(()) => println!("Request allowed"),
Err(_) => println!("Rate limited"),
}
// Check remaining capacity
println!("Remaining: {}", bucket.capacity_remaining().unwrap());
```
---
## Usage Examples
### Token Bucket (Burst-friendly)
Allows bursts up to capacity with sustained rate limiting:
```rust
use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;
let time_source = MockTimeSource::new();
let bucket = TokenBucketBuilder::builder()
.capacity(100)
.refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(bucket.try_acquire(50).is_ok());
time_source.advance(Duration::from_millis(200));
assert_eq!(bucket.capacity_remaining().unwrap(), 70); // 50 + 20 refill
```
### Fixed Window Counter
```rust
use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::FixedWindowCounterBuilder;
use std::time::Duration;
let counter = FixedWindowCounterBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(counter.try_acquire(50).is_ok());
```
### Sliding Window Counter
```rust
use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::SlidingWindowCounterBuilder;
use std::time::Duration;
let counter = SlidingWindowCounterBuilder::builder()
.capacity(100)
.bucket_duration(Duration::from_secs(10))
.bucket_count(6)
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(counter.try_acquire(25).is_ok());
```
### Approximate Sliding Window
```rust
use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::ApproximateSlidingWindowBuilder;
use std::time::Duration;
let window = ApproximateSlidingWindowBuilder::builder()
.capacity(100)
.window_duration(Duration::from_secs(60))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(window.try_acquire(30).is_ok());
```
---
## Production Usage
For detailed production environment integration examples, please refer to the [`examples/`](./examples/) directory:
- [`token_bucket_sync.rs`](./examples/token_bucket_sync.rs) - Token Bucket sync example
- [`token_bucket_async.rs`](./examples/token_bucket_async.rs) - Token Bucket async example
- [`fixed_window_counter_sync.rs`](./examples/fixed_window_counter_sync.rs) - Fixed window counter sync example
- [`fixed_window_counter_async.rs`](./examples/fixed_window_counter_async.rs) - Fixed window counter async example
- [`sliding_window_counter_sync.rs`](./examples/sliding_window_counter_sync.rs) - Sliding window counter sync example
- [`sliding_window_counter_async.rs`](./examples/sliding_window_counter_async.rs) - Sliding window counter async example
- [`approx_sliding_window_sync.rs`](./examples/approx_sliding_window_sync.rs) - Approximate sliding window sync example
- [`approx_sliding_window_async.rs`](./examples/approx_sliding_window_async.rs) - Approximate sliding window async example
### Recommended Production Configuration
```rust
#[cfg(feature = "std-time")]
use rate_guard::{Nanos, StdTimeSource, RateLimit};
#[cfg(feature = "std-time")]
use rate_guard::limits::TokenBucketBuilder;
#[cfg(feature = "std-time")]
use std::time::Duration;
#[cfg(feature = "std-time")]
fn create_production_limiter() -> impl RateLimit {
TokenBucketBuilder::builder()
.capacity(1000)
.refill_amount(100)
.refill_every(Duration::from_millis(100))
.with_time(StdTimeSource::new())
.with_precision::<Nanos>()
.build()
.unwrap()
}
```
> **Tip:** Start with conservative settings and adjust based on your actual requirements
---
## Verbose Error Handling
```rust
use rate_guard::{Millis, MockTimeSource, RateLimit, RateLimitError};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;
let bucket = TokenBucketBuilder::builder()
.capacity(10)
.refill_amount(1)
.refill_every(Duration::from_secs(1))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build()
.unwrap();
bucket.try_acquire(10).unwrap();
match bucket.try_acquire_verbose(5) {
Ok(()) => println!("Request allowed"),
Err(RateLimitError::InsufficientCapacity { acquiring, available, retry_after }) => {
println!("Need {} tokens, only {} available, retry after {:?}",
acquiring, available, retry_after);
}
Err(e) => println!("Other error: {}", e),
}
```
## Testing with Mock Time
```rust
use rate_guard::{Millis, MockTimeSource, RateLimit};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;
let time_source = MockTimeSource::new();
let bucket = TokenBucketBuilder::builder()
.capacity(50)
.refill_amount(5)
.refill_every(Duration::from_millis(200))
.with_time(time_source.clone())
.with_precision::<Millis>()
.build()
.unwrap();
assert!(bucket.try_acquire(50).is_ok());
assert_eq!(bucket.capacity_remaining().unwrap(), 0);
time_source.advance(Duration::from_millis(200));
assert_eq!(bucket.capacity_remaining().unwrap(), 5);
```
## Precision Types
```rust
use rate_guard::{Nanos, Micros, Millis, Secs, MockTimeSource};
use rate_guard::limits::TokenBucketBuilder;
use std::time::Duration;
let nano_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_nanos(100))
.with_time(MockTimeSource::new())
.with_precision::<Nanos>()
.build().unwrap();
let micro_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_micros(100))
.with_time(MockTimeSource::new())
.with_precision::<Micros>()
.build().unwrap();
let milli_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_millis(100))
.with_time(MockTimeSource::new())
.with_precision::<Millis>()
.build().unwrap();
let sec_bucket = TokenBucketBuilder::builder()
.capacity(100).refill_amount(10)
.refill_every(Duration::from_secs(1))
.with_time(MockTimeSource::new())
.with_precision::<Secs>()
.build().unwrap();
```
## Feature Flags
### Time Sources (Optional)
- `std-time` - Standard library time source (recommended for production)
- `tokio-time` - Tokio time source (for async environments)
### Precision Types (Mutually Exclusive, defaults to u64)
- `tick-u64` (default) - 64-bit precision, suitable for most scenarios
- `tick-u128` - 128-bit precision, for extreme long-duration use cases
```toml
# Default configuration
rate-guard = "0.1.0"
# Production recommended
rate-guard = { version = "0.1.0", features = ["std-time"] }
# Special requirements: high precision
rate-guard = { version = "0.1.0", features = ["std-time", "tick-u128"] }
```
## Minimum Supported Rust Version (MSRV)
This crate supports Rust **1.70.0** or newer.
### Feature-specific Requirements
- **Basic functionality**: Works with the specified MSRV
- **`tokio-time` feature**: Requires Rust 1.70.0+ (tokio requirement)
---
## License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
---
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.