use std::{
collections::HashSet,
sync::{Arc, Mutex},
};
#[derive(Clone, Default)]
pub struct DirtySet {
inner: Arc<Mutex<HashSet<u64>>>,
}
impl DirtySet {
pub fn new() -> Self {
Self::default()
}
pub fn mark_dirty(&self, id: u64) {
self.inner.lock().expect("DirtySet mutex poisoned").insert(id);
}
pub fn is_dirty(&self, id: u64) -> bool {
self.inner.lock().expect("DirtySet mutex poisoned").contains(&id)
}
pub fn clear(&self, id: u64) {
self.inner.lock().expect("DirtySet mutex poisoned").remove(&id);
}
pub fn drain_dirty(&self) -> Vec<u64> {
let mut guard = self.inner.lock().expect("DirtySet mutex poisoned");
guard.drain().collect()
}
pub fn len(&self) -> usize {
self.inner.lock().expect("DirtySet mutex poisoned").len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl std::fmt::Debug for DirtySet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let guard = self.inner.lock().expect("DirtySet mutex poisoned");
f.debug_set().entries(guard.iter()).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mark_and_check() {
let ds = DirtySet::new();
assert!(!ds.is_dirty(1));
ds.mark_dirty(1);
assert!(ds.is_dirty(1));
}
#[test]
fn clear_single() {
let ds = DirtySet::new();
ds.mark_dirty(1);
ds.mark_dirty(2);
ds.clear(1);
assert!(!ds.is_dirty(1));
assert!(ds.is_dirty(2));
}
#[test]
fn drain_returns_all_and_clears() {
let ds = DirtySet::new();
ds.mark_dirty(1);
ds.mark_dirty(2);
ds.mark_dirty(3);
let mut drained = ds.drain_dirty();
drained.sort();
assert_eq!(drained, vec![1, 2, 3]);
assert!(ds.is_empty());
}
#[test]
fn clone_shares_state() {
let ds = DirtySet::new();
let ds2 = ds.clone();
ds.mark_dirty(42);
assert!(ds2.is_dirty(42));
}
}