Skip to content

Commit b58bfd6

Browse files
committed
feat(api): stop exposing dashmap
1 parent c29fcd5 commit b58bfd6

5 files changed

Lines changed: 125 additions & 24 deletions

File tree

src/app/sub.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ where
202202
use std::path::{Path, PathBuf};
203203
let hardlink_info: Box<[(Size, Vec<PathBuf>)]> = record
204204
.iter()
205-
.map(|values| (values.0, values.1.clone()))
205+
.map(|values| (*values.size(), values.links().to_vec()))
206206
.collect();
207207
let hardlink_info: Box<[(Size, Vec<&Path>)]> = hardlink_info
208208
.iter()
@@ -218,9 +218,9 @@ where
218218
) -> Result<(), RuntimeError> {
219219
let (inodes, links, size): (usize, usize, Size) = report
220220
.iter()
221-
.filter_map(|ref_multi| {
222-
let (size, links) = &*ref_multi;
223-
let links = links.len();
221+
.filter_map(|values| {
222+
let size = values.size();
223+
let links = values.links().len();
224224
(links > 1).then_some(())?;
225225
Some((*size, links))
226226
})

src/hook.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ impl<Size, Reporter> Hook<Size, Reporter> for DoNothing {
3939

4040
// `RecordHardlink` is POSIX-exclusive, because whilst Windows does have `MetadataExt::number_of_links`, it requires Nightly.
4141
#[cfg(unix)]
42-
mod record_hardlink;
42+
pub mod record_hardlink;
4343
#[cfg(unix)]
4444
pub use record_hardlink::RecordHardLink;
4545
#[cfg(unix)]
46-
#[cfg(feature = "cli")]
47-
pub(crate) use record_hardlink::RecordHardLinkStorage;
46+
pub use record_hardlink::RecordHardLinkStorage;

src/hook/record_hardlink.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
pub mod storage;
2+
3+
pub use storage::RecordHardLinkStorage;
4+
15
use super::{Hook, HookArgument};
26
use crate::{
37
reporter::{event::EncounterHardlink, Event, Reporter},
48
size,
59
};
6-
use dashmap::DashMap;
7-
use std::{fmt::Debug, os::unix::fs::MetadataExt, path::PathBuf};
8-
9-
/// Map an inode number to its size and detected paths.
10-
pub type RecordHardLinkStorage<Size> = DashMap<u64, (Size, Vec<PathBuf>)>; // TODO: benchmark against Mutex<HashMap<u64, (Size, Vec<PathBuf>)>>
10+
use std::{fmt::Debug, os::unix::fs::MetadataExt};
1111

1212
/// A [hook](Hook) that record files with more than 1 links.
1313
#[derive(Debug, Clone, Copy)]
@@ -52,15 +52,6 @@ where
5252
links,
5353
}));
5454

55-
self.storage
56-
.entry(stats.ino())
57-
.and_modify(|(expected_size, paths)| {
58-
assert_eq!(
59-
size, *expected_size,
60-
"same ino but different sizes: {size:?} vs {expected_size:?}",
61-
);
62-
paths.push(path.to_path_buf());
63-
})
64-
.or_insert_with(|| (size, vec![path.to_path_buf()]));
55+
self.storage.add(stats.ino(), size, path).unwrap(); // TODO: propagate the error
6556
}
6657
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use crate::size;
2+
use dashmap::{iter::Iter as DashIter, mapref::multiple::RefMulti, DashMap};
3+
use derive_more::{Display, Error};
4+
use pipe_trait::Pipe;
5+
use std::{
6+
fmt::Debug,
7+
path::{Path, PathBuf},
8+
};
9+
10+
/// Storage to be used by [`crate::hook::RecordHardLink`].
11+
#[derive(Debug, Clone)]
12+
pub struct RecordHardLinkStorage<Size>(
13+
/// Map an inode number to its size and detected paths.
14+
DashMap<u64, (Size, Vec<PathBuf>)>, // TODO: benchmark against Mutex<HashMap<u64, (Size, Vec<PathBuf>)>>
15+
);
16+
17+
impl<Size> RecordHardLinkStorage<Size> {
18+
/// Create a new record.
19+
pub fn new() -> Self {
20+
RecordHardLinkStorage(DashMap::new())
21+
}
22+
23+
/// Iterate over the recorded entries.
24+
pub fn iter(&self) -> Iter<Size> {
25+
self.0.iter().pipe(Iter)
26+
}
27+
}
28+
29+
impl<Size> Default for RecordHardLinkStorage<Size> {
30+
fn default() -> Self {
31+
RecordHardLinkStorage::new()
32+
}
33+
}
34+
35+
/// Error that occurs when a different size was detected for the same [`ino`](std::os::unix::fs::MetadataExt::ino).
36+
#[derive(Debug, Display, Error)]
37+
#[display(bound(Size: Debug))]
38+
#[display("Size for inode {ino} changed from {recorded:?} to {detected:?}")]
39+
pub struct SizeConflictError<Size> {
40+
pub ino: u64, // TODO: define a newtype of ino.
41+
pub recorded: Size,
42+
pub detected: Size,
43+
}
44+
45+
/// Error type of [`RecordHardLinkStorage::add`].
46+
#[derive(Debug, Display, Error)]
47+
#[display(bound(Size: Debug))]
48+
#[non_exhaustive]
49+
pub enum AddError<Size> {
50+
SizeConflict(SizeConflictError<Size>),
51+
}
52+
53+
impl<Size> RecordHardLinkStorage<Size>
54+
where
55+
Size: size::Size,
56+
{
57+
/// Add an entry to the record.
58+
pub(crate) fn add(&self, ino: u64, size: Size, path: &Path) -> Result<(), AddError<Size>> {
59+
let mut size_assertion = Ok(());
60+
self.0
61+
.entry(ino)
62+
.and_modify(|(recorded, paths)| {
63+
let (detected, recorded) = (size, *recorded);
64+
if size == recorded {
65+
paths.push(path.to_path_buf());
66+
} else {
67+
size_assertion = Err(SizeConflictError {
68+
ino,
69+
recorded,
70+
detected,
71+
});
72+
}
73+
})
74+
.or_insert_with(|| (size, vec![path.to_path_buf()]));
75+
size_assertion.map_err(AddError::SizeConflict)
76+
}
77+
}
78+
79+
/// Iterator over entries in [`RecordHardLinkStorage`].
80+
#[derive(derive_more::Debug)]
81+
#[debug(bound())]
82+
#[debug("Iter(..)")]
83+
pub struct Iter<'a, Size>(DashIter<'a, u64, (Size, Vec<PathBuf>)>);
84+
85+
/// [Item](Iterator::Item) of [`Iter`].
86+
#[derive(derive_more::Debug)]
87+
#[debug(bound())]
88+
#[debug("IterItem(..)")]
89+
pub struct IterItem<'a, Size>(RefMulti<'a, u64, (Size, Vec<PathBuf>)>);
90+
91+
impl<'a, Size> Iterator for Iter<'a, Size> {
92+
type Item = IterItem<'a, Size>;
93+
fn next(&mut self) -> Option<Self::Item> {
94+
self.0.next().map(IterItem)
95+
}
96+
}
97+
98+
impl<'a, Size> IterItem<'a, Size> {
99+
/// Number of the inode.
100+
pub fn ino(&self) -> u64 {
101+
*self.0.key()
102+
}
103+
104+
/// Size of the inode.
105+
pub fn size(&self) -> &Size {
106+
&self.0.value().0
107+
}
108+
109+
/// Links of the inode.
110+
pub fn links(&self) -> &[PathBuf] {
111+
&self.0.value().1
112+
}
113+
}

src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ pub use clap;
2828
pub use clap_complete;
2929
#[cfg(feature = "cli")]
3030
pub use clap_utilities;
31-
#[cfg(unix)]
32-
pub use dashmap;
3331

3432
pub mod bytes_format;
3533
pub mod data_tree;

0 commit comments

Comments
 (0)