Skip to main content

cloud_sdk/
rate_limit.rs

1//! Provider-neutral rate-limit response metadata.
2
3/// Rate-limit metadata validation error.
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum RateLimitError {
6    /// A reported request limit must be nonzero.
7    LimitZero,
8    /// Remaining requests must not exceed the reported limit.
9    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/// Validated rate-limit metadata returned by a transport.
18#[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    /// Creates coherent rate-limit metadata.
27    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    /// Returns the total request limit for the provider's time frame.
46    #[must_use]
47    pub const fn limit(self) -> u64 {
48        self.limit
49    }
50
51    /// Returns the number of requests remaining in the current time frame.
52    #[must_use]
53    pub const fn remaining(self) -> u64 {
54        self.remaining
55    }
56
57    /// Returns the provider reset time as Unix epoch seconds.
58    #[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}