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