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
12/// Validated rate-limit metadata returned by a transport.
13#[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    /// Creates coherent rate-limit metadata.
22    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    /// Returns the total request limit for the provider's time frame.
41    #[must_use]
42    pub const fn limit(self) -> u64 {
43        self.limit
44    }
45
46    /// Returns the number of requests remaining in the current time frame.
47    #[must_use]
48    pub const fn remaining(self) -> u64 {
49        self.remaining
50    }
51
52    /// Returns the provider reset time as Unix epoch seconds.
53    #[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}