1mod decision;
4mod extension;
5mod http_date;
6mod quota;
7mod time;
8
9pub use decision::{
10 DelayConflictPolicy, DelayDecision, DelayDecisionError, DelaySource, ExcessDelayPolicy,
11 PastTimestampPolicy, QuotaDelayPolicy, decide_delay,
12};
13pub use extension::{
14 MAX_QUOTA_EXTENSION_NAME_BYTES, MAX_QUOTA_EXTENSION_VALUE_BYTES, QuotaExtension,
15 QuotaExtensionError,
16};
17pub use quota::{
18 MAX_QUOTA_BUCKET_ID_BYTES, MAX_QUOTA_BUCKETS, MAX_QUOTA_EXTENSIONS_PER_BUCKET, QuotaBucket,
19 QuotaBucketId, QuotaBuckets, QuotaError, QuotaReset,
20};
21pub use time::{DelaySeconds, HttpDate, RetryAfter, RetryAfterError, WallClockTimestamp};
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub enum RateLimitError {
26 LimitZero,
28 RemainingExceedsLimit,
30}
31
32impl_static_error!(RateLimitError,
33 Self::LimitZero => "rate limit must be nonzero",
34 Self::RemainingExceedsLimit => "remaining requests exceed the rate limit",
35);
36
37#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct RateLimit {
43 limit: u64,
44 remaining: u64,
45 reset_epoch_seconds: u64,
46}
47
48impl RateLimit {
49 pub const fn new(
51 limit: u64,
52 remaining: u64,
53 reset_epoch_seconds: u64,
54 ) -> Result<Self, RateLimitError> {
55 if limit == 0 {
56 return Err(RateLimitError::LimitZero);
57 }
58 if remaining > limit {
59 return Err(RateLimitError::RemainingExceedsLimit);
60 }
61 Ok(Self {
62 limit,
63 remaining,
64 reset_epoch_seconds,
65 })
66 }
67
68 #[must_use]
70 pub const fn limit(self) -> u64 {
71 self.limit
72 }
73
74 #[must_use]
76 pub const fn remaining(self) -> u64 {
77 self.remaining
78 }
79
80 #[must_use]
82 pub const fn reset_epoch_seconds(self) -> u64 {
83 self.reset_epoch_seconds
84 }
85}
86
87#[cfg(test)]
88mod legacy_tests {
89 use super::{RateLimit, RateLimitError};
90
91 #[test]
92 fn rejects_incoherent_metadata() {
93 assert_eq!(RateLimit::new(0, 0, 42), Err(RateLimitError::LimitZero));
94 assert_eq!(
95 RateLimit::new(10, 11, 42),
96 Err(RateLimitError::RemainingExceedsLimit)
97 );
98 }
99
100 #[test]
101 fn exposes_coherent_metadata() {
102 let rate_limit = RateLimit::new(3600, 3599, 42);
103 assert!(rate_limit.is_ok());
104 let Ok(rate_limit) = rate_limit else { return };
105 assert_eq!(rate_limit.limit(), 3600);
106 assert_eq!(rate_limit.remaining(), 3599);
107 assert_eq!(rate_limit.reset_epoch_seconds(), 42);
108 }
109}
110
111#[cfg(test)]
112mod tests;