1use governor::{Quota, RateLimiter};
4use std::num::NonZeroU32;
5use std::sync::Arc;
6use tracing::debug;
7
8pub type YfRateLimiter = RateLimiter<
10 governor::state::NotKeyed,
11 governor::state::InMemoryState,
12 governor::clock::DefaultClock,
13>;
14
15pub const DEFAULT_REQUESTS_PER_MINUTE: u32 = 5;
17
18#[derive(Debug, Clone)]
20pub struct RateLimitConfig {
21 pub requests_per_minute: u32,
23}
24
25impl Default for RateLimitConfig {
26 fn default() -> Self {
27 Self {
28 requests_per_minute: DEFAULT_REQUESTS_PER_MINUTE,
29 }
30 }
31}
32
33impl RateLimitConfig {
34 pub fn new(requests_per_minute: u32) -> Self {
36 Self { requests_per_minute }
37 }
38
39 pub fn build_limiter(&self) -> Arc<YfRateLimiter> {
41 let quota = Quota::per_minute(
42 NonZeroU32::new(self.requests_per_minute).unwrap_or(NonZeroU32::new(1).unwrap())
43 );
44 Arc::new(RateLimiter::direct(quota))
45 }
46}
47
48pub async fn wait_for_permit(limiter: &YfRateLimiter) {
50 limiter.until_ready().await;
51 debug!("Rate limit permit acquired");
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn test_default_config() {
60 let config = RateLimitConfig::default();
61 assert_eq!(config.requests_per_minute, 5);
62 }
63
64 #[test]
65 fn test_custom_config() {
66 let config = RateLimitConfig::new(10);
67 assert_eq!(config.requests_per_minute, 10);
68 }
69
70 #[test]
71 fn test_build_limiter() {
72 let config = RateLimitConfig::new(5);
73 let limiter = config.build_limiter();
74 assert!(limiter.check().is_ok());
75 }
76
77 #[test]
78 fn test_rate_limiter_zero_fallback() {
79 let config = RateLimitConfig::new(0);
81 let limiter = config.build_limiter();
82 assert!(limiter.check().is_ok());
84 }
85}