1use std::collections::HashMap;
2
3#[derive(Debug, Default, Clone)]
4pub struct InodeFilter {
5 inner: HashMap<(u64, u64), u64>,
6}
7
8impl InodeFilter {
9 #[cfg(unix)]
10 pub fn add(&mut self, metadata: &std::fs::Metadata) -> bool {
11 use std::os::unix::fs::MetadataExt;
12
13 self.add_dev_inode((metadata.dev(), metadata.ino()), metadata.nlink())
14 }
15
16 #[cfg(windows)]
17 pub fn add(&mut self, metadata: &std::fs::Metadata) -> bool {
18 use std::os::windows::fs::MetadataExt;
19
20 if let (Some(dev), Some(inode), Some(nlinks)) = (
21 metadata.volume_serial_number(),
22 metadata.file_index(),
23 metadata.number_of_links(),
24 ) {
25 self.add_dev_inode((dev as u64, inode), nlinks as u64)
26 } else {
27 true
28 }
29 }
30
31 #[cfg(not(any(unix, windows)))]
32 pub fn add(&mut self, metadata: &std::fs::Metadata) -> bool {
33 true
34 }
35
36 pub fn add_dev_inode(&mut self, dev_inode: (u64, u64), nlinks: u64) -> bool {
37 if nlinks <= 1 {
38 return true;
39 }
40
41 match self.inner.get_mut(&dev_inode) {
42 Some(1) => {
43 self.inner.remove(&dev_inode);
44 false
45 }
46 Some(count) => {
47 *count -= 1;
48 false
49 }
50 None => {
51 self.inner.insert(dev_inode, nlinks - 1);
52 true
53 }
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn it_filters_inodes() {
64 let mut inodes = InodeFilter::default();
65
66 assert!(inodes.add_dev_inode((1, 1), 2));
67 assert!(inodes.add_dev_inode((2, 1), 2));
68 assert!(!inodes.add_dev_inode((1, 1), 2));
69 assert!(!inodes.add_dev_inode((2, 1), 2));
70
71 assert!(inodes.add_dev_inode((1, 1), 3));
72 assert!(!inodes.add_dev_inode((1, 1), 3));
73 assert!(!inodes.add_dev_inode((1, 1), 3));
74
75 assert!(inodes.add_dev_inode((1, 1), 1));
76 assert!(inodes.add_dev_inode((1, 1), 1));
77 }
78}