lazy_limit/
types.rs

1/* src/types.rs */
2
3use std::time::{SystemTime, UNIX_EPOCH};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
6pub enum Duration {
7    Seconds(u64),
8    Minutes(u64),
9    Hours(u64),
10    Days(u64),
11}
12
13impl Duration {
14    pub fn seconds(n: u64) -> Self {
15        Duration::Seconds(n)
16    }
17    pub fn minutes(n: u64) -> Self {
18        Duration::Minutes(n)
19    }
20    pub fn hours(n: u64) -> Self {
21        Duration::Hours(n)
22    }
23    pub fn days(n: u64) -> Self {
24        Duration::Days(n)
25    }
26
27    pub fn as_seconds(&self) -> u64 {
28        match self {
29            Duration::Seconds(n) => *n,
30            Duration::Minutes(n) => n * 60,
31            Duration::Hours(n) => n * 3600,
32            Duration::Days(n) => n * 86400,
33        }
34    }
35
36    pub fn is_short_interval(&self) -> bool {
37        self.as_seconds() <= 300 // 5 minutes
38    }
39}
40
41#[derive(Debug, Clone)]
42pub struct RuleConfig {
43    pub interval: Duration,
44    pub limit: u32,
45}
46
47impl RuleConfig {
48    pub fn new(interval: Duration, limit: u32) -> Self {
49        Self { interval, limit }
50    }
51}
52
53#[derive(Debug, Clone)]
54pub struct RequestRecord {
55    pub count: u32,
56    pub window_start: u64,
57    pub timestamps: Vec<u64>,
58}
59
60impl RequestRecord {
61    pub fn new(is_short_interval: bool) -> Self {
62        Self {
63            count: 0,
64            window_start: current_timestamp(),
65            timestamps: if is_short_interval {
66                Vec::new()
67            } else {
68                Vec::with_capacity(16)
69            },
70        }
71    }
72
73    pub fn add_request(&mut self, is_short_interval: bool, window_size: u64) {
74        let now = current_timestamp();
75
76        if is_short_interval {
77            if now.saturating_sub(self.window_start) >= window_size {
78                self.window_start = now;
79                self.count = 1;
80            } else {
81                self.count += 1;
82            }
83        } else {
84            self.timestamps.push(now);
85            let cutoff = now.saturating_sub(window_size);
86            self.timestamps.retain(|&t| t > cutoff);
87            self.count = self.timestamps.len() as u32;
88        }
89    }
90
91    pub fn is_limit_exceeded(&self, limit: u32, is_short_interval: bool, window_size: u64) -> bool {
92        let now = current_timestamp();
93        if is_short_interval {
94            if now.saturating_sub(self.window_start) >= window_size {
95                false
96            } else {
97                self.count >= limit
98            }
99        } else {
100            let cutoff = now.saturating_sub(window_size);
101            let valid_requests = self.timestamps.iter().filter(|&&t| t > cutoff).count() as u32;
102            valid_requests >= limit
103        }
104    }
105
106    pub fn memory_usage(&self) -> usize {
107        std::mem::size_of::<Self>() + self.timestamps.capacity() * std::mem::size_of::<u64>()
108    }
109
110    pub fn should_cleanup(&self, max_age_seconds: u64) -> bool {
111        let now = current_timestamp();
112        let last_activity = if !self.timestamps.is_empty() {
113            *self.timestamps.last().unwrap_or(&self.window_start)
114        } else {
115            self.window_start
116        };
117        now.saturating_sub(last_activity) > max_age_seconds
118    }
119}
120
121pub fn current_timestamp() -> u64 {
122    SystemTime::now()
123        .duration_since(UNIX_EPOCH)
124        .expect("Time went backwards")
125        .as_secs()
126}