use gravityfile_core::InodeInfo;
use std::collections::HashSet;
#[derive(Debug, Default)]
pub struct InodeTracker {
seen: HashSet<InodeInfo>,
track_single_links: bool,
}
impl InodeTracker {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn with_alias_tracking() -> Self {
Self {
track_single_links: true,
..Self::default()
}
}
pub fn track(&mut self, info: InodeInfo, nlink: u64) -> bool {
if self.track_single_links || nlink > 1 {
self.seen.insert(info)
} else {
!self.seen.contains(&info)
}
}
#[doc(hidden)]
pub fn pending_count(&self) -> usize {
self.seen.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn changing_link_counts_and_extra_aliases_do_not_recount_seen_identity() {
let mut tracker = InodeTracker::new();
let info = InodeInfo::new(42, 1);
assert!(tracker.track(info, 2));
for count in [2, 1, 0, 3, 2] {
assert!(!tracker.track(info, count));
}
let mut aliases = InodeTracker::with_alias_tracking();
assert!(aliases.track(info, 1));
assert!(!aliases.track(info, 1));
}
#[test]
fn test_track_new_inode_nlink_1() {
let mut tracker = InodeTracker::new();
let info = InodeInfo::new(12345, 1);
assert!(tracker.track(info, 1));
assert!(tracker.track(info, 1));
}
#[test]
fn test_track_hardlink_nlink_2() {
let mut tracker = InodeTracker::new();
let info = InodeInfo::new(12345, 1);
assert!(tracker.track(info, 2));
assert!(!tracker.track(info, 2));
assert_eq!(tracker.pending_count(), 1);
}
#[test]
fn test_track_hardlink_nlink_3() {
let mut tracker = InodeTracker::new();
let info = InodeInfo::new(99, 2);
assert!(tracker.track(info, 3));
assert!(!tracker.track(info, 3));
assert!(!tracker.track(info, 3));
assert_eq!(tracker.pending_count(), 1);
}
#[test]
fn test_different_devices() {
let mut tracker = InodeTracker::new();
let info1 = InodeInfo::new(12345, 1);
let info2 = InodeInfo::new(12345, 2);
assert!(tracker.track(info1, 2));
assert!(tracker.track(info2, 2)); }
}