1use std::collections::VecDeque;
4use std::time::{Duration, Instant};
5
6pub const BACKOFF_MAX: Duration = Duration::from_secs(300);
8
9pub fn backoff(base: Duration, attempt: u32, retry_after: Option<Duration>) -> Duration {
12 let exp = base.saturating_mul(1u32 << attempt.saturating_sub(1).min(16));
13 exp.max(retry_after.unwrap_or_default()).min(BACKOFF_MAX)
14}
15
16#[derive(Debug, Default)]
18pub struct RateLimit {
19 sent: VecDeque<Instant>,
20}
21
22impl RateLimit {
23 pub fn wait(&mut self, now: Instant, per_minute: usize) -> Duration {
25 while self
26 .sent
27 .front()
28 .is_some_and(|t| now.duration_since(*t) >= Duration::from_secs(60))
29 {
30 self.sent.pop_front();
31 }
32 if self.sent.len() < per_minute {
33 return Duration::ZERO;
34 }
35 (self.sent[0] + Duration::from_secs(60)).saturating_duration_since(now)
36 }
37
38 pub fn record(&mut self, now: Instant) {
39 self.sent.push_back(now);
40 }
41}