1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
mod private
{
use std::sync::{ Arc, Mutex };
use std::time::{ Duration, Instant };
use tokio::time::sleep;
/// Rate limiter configuration using token bucket algorithm.
///
/// Controls the rate of API requests to prevent exceeding rate limits.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiterConfig;
///
/// // 100 requests per minute
/// let config = RateLimiterConfig::new( 100, 60 );
/// ```
#[ derive( Debug, Clone ) ]
pub struct RateLimiterConfig
{
/// Maximum number of tokens in the bucket.
pub capacity : usize,
/// Number of tokens to refill per second.
pub refill_rate : f64,
}
impl RateLimiterConfig
{
/// Creates a new rate limiter configuration.
///
/// # Arguments
///
/// * `requests` - Maximum number of requests
/// * `period_seconds` - Time period in seconds
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiterConfig;
///
/// // 60 requests per minute
/// let config = RateLimiterConfig::new( 60, 60 );
/// ```
pub fn new( requests : usize, period_seconds : u64 ) -> Self
{
Self
{
capacity : requests,
refill_rate : requests as f64 / period_seconds as f64,
}
}
/// Creates a limiter for requests per second.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiterConfig;
///
/// // 10 requests per second
/// let config = RateLimiterConfig::per_second( 10 );
/// ```
pub fn per_second( requests : usize ) -> Self
{
Self::new( requests, 1 )
}
/// Creates a limiter for requests per minute.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiterConfig;
///
/// // 100 requests per minute
/// let config = RateLimiterConfig::per_minute( 100 );
/// ```
pub fn per_minute( requests : usize ) -> Self
{
Self::new( requests, 60 )
}
/// Creates a limiter for requests per hour.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiterConfig;
///
/// // 1000 requests per hour
/// let config = RateLimiterConfig::per_hour( 1000 );
/// ```
pub fn per_hour( requests : usize ) -> Self
{
Self::new( requests, 3600 )
}
}
impl Default for RateLimiterConfig
{
fn default() -> Self
{
// Default : 100 requests per minute
Self::per_minute( 100 )
}
}
/// Token bucket rate limiter.
///
/// Implements the token bucket algorithm for rate limiting API requests.
/// Tokens are refilled at a constant rate, and requests consume tokens.
///
/// # Examples
///
/// ```
/// use api_xai::{ RateLimiter, RateLimiterConfig };
///
/// # async fn example() {
/// let limiter = RateLimiter::new( RateLimiterConfig::per_second( 10 ) );
///
/// // Acquire permission before making request
/// limiter.acquire().await;
/// // Make API request
/// # }
/// ```
#[ derive( Debug, Clone ) ]
pub struct RateLimiter
{
config : RateLimiterConfig,
state : Arc< Mutex< RateLimiterState > >,
}
#[ derive( Debug ) ]
struct RateLimiterState
{
tokens : f64,
last_refill : Instant,
}
impl RateLimiter
{
/// Creates a new rate limiter with the given configuration.
///
/// # Examples
///
/// ```
/// use api_xai::{ RateLimiter, RateLimiterConfig };
///
/// let limiter = RateLimiter::new( RateLimiterConfig::per_minute( 100 ) );
/// ```
pub fn new( config : RateLimiterConfig ) -> Self
{
Self
{
state : Arc::new( Mutex::new( RateLimiterState
{
tokens : config.capacity as f64,
last_refill : Instant::now(),
} ) ),
config,
}
}
/// Refills tokens based on elapsed time.
fn refill( &self, state : &mut RateLimiterState )
{
let now = Instant::now();
let elapsed = now.duration_since( state.last_refill ).as_secs_f64();
// Add tokens based on refill rate and elapsed time
state.tokens += elapsed * self.config.refill_rate;
// Cap at capacity
if state.tokens > self.config.capacity as f64
{
state.tokens = self.config.capacity as f64;
}
state.last_refill = now;
}
/// Acquires a token, waiting if necessary.
///
/// Blocks until a token is available. Returns immediately if a token
/// is available, otherwise sleeps until one is refilled.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiter;
///
/// # async fn example() {
/// let limiter = RateLimiter::default();
///
/// // Wait for permission to proceed
/// limiter.acquire().await;
///
/// // Make API request
/// # }
/// ```
///
/// # Panics
///
/// Panics if the internal mutex is poisoned.
pub async fn acquire( &self )
{
loop
{
let wait_time = {
let mut state = self.state.lock().unwrap();
self.refill( &mut state );
if state.tokens >= 1.0
{
state.tokens -= 1.0;
return;
}
// Calculate time to wait for next token
let tokens_needed = 1.0 - state.tokens;
Duration::from_secs_f64( tokens_needed / self.config.refill_rate )
// MutexGuard dropped here when scope ends
};
// Sleep and retry (guard is already dropped)
sleep( wait_time ).await;
}
}
/// Tries to acquire a token without waiting.
///
/// Returns `true` if a token was acquired, `false` if none available.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiter;
///
/// let limiter = RateLimiter::default();
///
/// if limiter.try_acquire() {
/// // Token acquired, proceed with request
/// } else {
/// // No tokens available, skip or queue request
/// }
/// ```
///
/// # Panics
///
/// Panics if the internal mutex is poisoned.
pub fn try_acquire( &self ) -> bool
{
let mut state = self.state.lock().unwrap();
self.refill( &mut state );
if state.tokens >= 1.0
{
state.tokens -= 1.0;
true
}
else
{
false
}
}
/// Returns the current number of available tokens.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiter;
///
/// let limiter = RateLimiter::default();
/// let available = limiter.available_tokens();
/// ```
///
/// # Panics
///
/// Panics if the internal mutex is poisoned.
#[ allow( clippy::cast_possible_truncation, clippy::cast_sign_loss) ] // Tokens always non-negative, bounded by capacity
pub fn available_tokens( &self ) -> usize
{
let mut state = self.state.lock().unwrap();
self.refill( &mut state );
state.tokens.max( 0.0 ).floor() as usize
}
/// Resets the rate limiter to full capacity.
///
/// # Examples
///
/// ```
/// use api_xai::RateLimiter;
///
/// let limiter = RateLimiter::default();
/// limiter.reset();
/// ```
///
/// # Panics
///
/// Panics if the internal mutex is poisoned.
pub fn reset( &self )
{
let mut state = self.state.lock().unwrap();
state.tokens = self.config.capacity as f64;
state.last_refill = Instant::now();
}
}
impl Default for RateLimiter
{
fn default() -> Self
{
Self::new( RateLimiterConfig::default() )
}
}
}
crate::mod_interface!
{
exposed use
{
RateLimiterConfig,
RateLimiter,
};
}