1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum RateLimitError {
6 LimitZero,
8 RemainingExceedsLimit,
10}
11
12impl_static_error!(RateLimitError,
13 Self::LimitZero => "rate limit must be nonzero",
14 Self::RemainingExceedsLimit => "remaining requests exceed the rate limit",
15);
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub struct RateLimit {
20 limit: u64,
21 remaining: u64,
22 reset_epoch_seconds: u64,
23}
24
25impl RateLimit {
26 pub const fn new(
28 limit: u64,
29 remaining: u64,
30 reset_epoch_seconds: u64,
31 ) -> Result<Self, RateLimitError> {
32 if limit == 0 {
33 return Err(RateLimitError::LimitZero);
34 }
35 if remaining > limit {
36 return Err(RateLimitError::RemainingExceedsLimit);
37 }
38 Ok(Self {
39 limit,
40 remaining,
41 reset_epoch_seconds,
42 })
43 }
44
45 #[must_use]
47 pub const fn limit(self) -> u64 {
48 self.limit
49 }
50
51 #[must_use]
53 pub const fn remaining(self) -> u64 {
54 self.remaining
55 }
56
57 #[must_use]
59 pub const fn reset_epoch_seconds(self) -> u64 {
60 self.reset_epoch_seconds
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::{RateLimit, RateLimitError};
67
68 #[test]
69 fn rejects_incoherent_metadata() {
70 assert_eq!(RateLimit::new(0, 0, 42), Err(RateLimitError::LimitZero));
71 assert_eq!(
72 RateLimit::new(10, 11, 42),
73 Err(RateLimitError::RemainingExceedsLimit)
74 );
75 }
76
77 #[test]
78 fn exposes_coherent_metadata() {
79 let rate_limit = RateLimit::new(3600, 3599, 42);
80 assert!(rate_limit.is_ok());
81 let Ok(rate_limit) = rate_limit else { return };
82 assert_eq!(rate_limit.limit(), 3600);
83 assert_eq!(rate_limit.remaining(), 3599);
84 assert_eq!(rate_limit.reset_epoch_seconds(), 42);
85 }
86}