use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
pub struct Dup {
messages: Arc<RwLock<HashMap<String, MessageEntry>>>,
max_age: Duration,
max_size: usize,
}
#[derive(Clone, Debug)]
struct MessageEntry {
was: Instant,
via: Option<String>, it: Option<serde_json::Value>, }
impl Dup {
pub fn new(max_size: usize, max_age_ms: u64) -> Self {
Self {
messages: Arc::new(RwLock::new(HashMap::new())),
max_age: Duration::from_millis(max_age_ms),
max_size,
}
}
pub fn new_default() -> Self {
Self::new(999, 9000)
}
pub fn check(&self, id: &str) -> bool {
let messages = self.messages.read().expect("Dup lock poisoned");
if let Some(entry) = messages.get(id) {
if entry.was.elapsed() < self.max_age {
return true; }
}
false }
pub fn track(&mut self, id: &str) -> bool {
let mut messages = self.messages.write().expect("Dup lock poisoned");
if messages.len() >= self.max_size {
self.drop_expired(&mut messages);
}
let entry = messages
.entry(id.to_string())
.or_insert_with(|| MessageEntry {
was: Instant::now(),
via: None,
it: None,
});
entry.was = Instant::now();
true
}
pub fn track_with_peer(&mut self, id: &str, peer_id: Option<&str>) {
let mut messages = self.messages.write().expect("Dup lock poisoned");
let entry = messages
.entry(id.to_string())
.or_insert_with(|| MessageEntry {
was: Instant::now(),
via: None,
it: None,
});
entry.was = Instant::now();
if let Some(pid) = peer_id {
entry.via = Some(pid.to_string());
}
}
fn drop_expired(&self, messages: &mut HashMap<String, MessageEntry>) {
let now = Instant::now();
messages.retain(|_, entry| now.duration_since(entry.was) < self.max_age);
}
pub fn drop_expired_all(&self) {
let mut messages = self.messages.write().expect("Dup lock poisoned");
self.drop_expired(&mut messages);
}
pub fn get_via(&self, id: &str) -> Option<String> {
let messages = self.messages.read().expect("Dup lock poisoned");
messages.get(id).and_then(|e| e.via.clone())
}
pub fn store(&mut self, id: &str, data: serde_json::Value) {
let mut messages = self.messages.write().expect("Dup lock poisoned");
if let Some(entry) = messages.get_mut(id) {
entry.it = Some(data);
} else {
messages.insert(
id.to_string(),
MessageEntry {
was: Instant::now(),
via: None,
it: Some(data),
},
);
}
}
pub fn get(&self, id: &str) -> Option<serde_json::Value> {
let messages = self.messages.read().expect("Dup lock poisoned");
messages.get(id).and_then(|e| e.it.clone())
}
pub fn remove(&self, id: &str) {
let mut messages = self.messages.write().expect("Dup lock poisoned");
messages.remove(id);
}
}
impl Default for Dup {
fn default() -> Self {
Self::new_default()
}
}