"""Data models for rate limiting.""" from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Any class KeyType(str, Enum): """Type of key extraction for rate limiting.""" IP = "ip" USER = "user" API_KEY = "api_key" ENDPOINT = "endpoint" CUSTOM = "custom" @dataclass(frozen=True, slots=True) class RateLimitInfo: """Information about the current rate limit state.""" limit: int remaining: int reset_at: float retry_after: float | None = None window_size: float = 60.0 def to_headers(self) -> dict[str, str]: """Convert rate limit info to HTTP headers.""" headers: dict[str, str] = { "X-RateLimit-Limit": str(self.limit), "X-RateLimit-Remaining": str(max(0, self.remaining)), "X-RateLimit-Reset": str(int(self.reset_at)), } if self.retry_after is not None: headers["Retry-After"] = str(int(self.retry_after)) return headers @dataclass(frozen=True, slots=True) class RateLimitResult: """Result of a rate limit check.""" allowed: bool info: RateLimitInfo key: str @dataclass(slots=True) class TokenBucketState: """State for token bucket algorithm.""" tokens: float last_update: float @dataclass(slots=True) class SlidingWindowState: """State for sliding window algorithm.""" timestamps: list[float] = field(default_factory=list) count: int = 0 @dataclass(slots=True) class FixedWindowState: """State for fixed window algorithm.""" count: int window_start: float @dataclass(slots=True) class LeakyBucketState: """State for leaky bucket algorithm.""" water_level: float last_update: float @dataclass(frozen=True, slots=True) class BackendRecord: """Generic record stored in backends.""" key: str data: dict[str, Any] expires_at: float