imessage_webhooks/
event_cache.rs1use std::collections::HashMap;
6use std::time::{Duration, Instant};
7
8const TTL: Duration = Duration::from_secs(3600); pub struct EventCache {
11 entries: HashMap<String, Instant>,
12}
13
14impl EventCache {
15 pub fn new() -> Self {
16 Self {
17 entries: HashMap::new(),
18 }
19 }
20
21 pub fn is_duplicate(&mut self, key: &str) -> bool {
24 self.cleanup();
25
26 if self.entries.contains_key(key) {
27 return true;
28 }
29
30 self.entries.insert(key.to_string(), Instant::now());
31 false
32 }
33
34 fn cleanup(&mut self) {
36 let now = Instant::now();
37 self.entries.retain(|_, ts| now.duration_since(*ts) < TTL);
38 }
39
40 pub fn len(&self) -> usize {
42 self.entries.len()
43 }
44
45 pub fn is_empty(&self) -> bool {
46 self.entries.is_empty()
47 }
48}
49
50impl Default for EventCache {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn new_events_are_not_duplicates() {
62 let mut cache = EventCache::new();
63 assert!(!cache.is_duplicate("event-1"));
64 assert!(!cache.is_duplicate("event-2"));
65 assert_eq!(cache.len(), 2);
66 }
67
68 #[test]
69 fn repeated_events_are_duplicates() {
70 let mut cache = EventCache::new();
71 assert!(!cache.is_duplicate("event-1"));
72 assert!(cache.is_duplicate("event-1"));
73 assert!(cache.is_duplicate("event-1"));
74 }
75
76 #[test]
77 fn different_events_are_independent() {
78 let mut cache = EventCache::new();
79 assert!(!cache.is_duplicate("event-1"));
80 assert!(!cache.is_duplicate("event-2"));
81 assert!(cache.is_duplicate("event-1"));
82 assert!(cache.is_duplicate("event-2"));
83 }
84}