rate-guard 0.1.0

Thread-safe rate limiting library with multiple algorithms and Duration-based configuration
Documentation
# Rate-Guard


A flexible and type-safe rate limiting library for Rust.

[![Crates.io](https://img.shields.io/crates/v/rate-guard.svg)](https://crates.io/crates/rate-guard)
[![Documentation](https://docs.rs/rate-guard/badge.svg)](https://docs.rs/rate-guard)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](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.