Skip to content

Commit 32fe9b6

Browse files
committed
feat: report the number of unrecorded links (wip)
1 parent 47d5cd5 commit 32fe9b6

5 files changed

Lines changed: 74 additions & 36 deletions

File tree

src/hardlink/aware.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ where
8383

8484
let ino = InodeNumber::get(stats);
8585
self.record
86-
.add(ino, size, path)
86+
.add(ino, size, links, path)
8787
.map_err(ReportHardlinksError::AddToRecord)
8888
}
8989
}
@@ -102,7 +102,7 @@ where
102102
let record: Self::Report = self.into();
103103
let hardlink_info: Box<[(Size, LinkPathList)]> = record
104104
.iter()
105-
.map(|values| (*values.size(), values.links().clone()))
105+
.map(|values| (*values.size(), values.paths().clone()))
106106
.collect();
107107
let hardlink_info: Box<[(Size, Vec<&Path>)]> = hardlink_info
108108
.iter()

src/hardlink/hardlink_list.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,26 @@ use pipe_trait::Pipe;
1616
use smart_default::SmartDefault;
1717
use std::{fmt::Debug, path::Path};
1818

19+
/// Map value in [`HardlinkList`].
20+
#[derive(Debug, Clone)]
21+
struct Value<Size> {
22+
/// The size of the file.
23+
size: Size,
24+
/// Total number of links of the file, both listed (in [`Self::paths`]) and unlisted.
25+
links: u64,
26+
/// Paths to the detected links of the file.
27+
paths: LinkPathList,
28+
}
29+
1930
/// Storage to be used by [`crate::hardlink::RecordHardlinks`].
2031
///
2132
/// **Serialization and deserialization:** _(feature: `json`)_ `HardlinkList` does not implement
2233
/// `Serialize` and `Deserialize` traits directly, instead, it can be converted into/from a
2334
/// [`Reflection`] which implements these traits.
2435
#[derive(Debug, SmartDefault, Clone)]
2536
pub struct HardlinkList<Size>(
26-
/// Map an inode number to its size and detected paths.
27-
DashMap<InodeNumber, (Size, LinkPathList)>, // TODO: benchmark against Mutex<HashMap<InodeNumber, (Size, LinkPathList)>>
37+
/// Map an inode number to its size, number of links, and detected paths.
38+
DashMap<InodeNumber, Value<Size>>, // TODO: benchmark against Mutex<HashMap<InodeNumber, Value<Size>>>
2839
);
2940

3041
impl<Size> HardlinkList<Size> {
@@ -76,24 +87,27 @@ where
7687
&self,
7788
ino: InodeNumber,
7889
size: Size,
90+
links: u64,
7991
path: &Path,
8092
) -> Result<(), AddError<Size>> {
8193
let mut size_assertion = Ok(());
8294
self.0
8395
.entry(ino)
84-
.and_modify(|(recorded, paths)| {
85-
let (detected, recorded) = (size, *recorded);
86-
if size == recorded {
87-
paths.add(path.to_path_buf());
96+
.and_modify(|recorded| {
97+
if size == recorded.size {
98+
recorded.paths.add(path.to_path_buf());
8899
} else {
89100
size_assertion = Err(SizeConflictError {
90101
ino,
91-
recorded,
92-
detected,
102+
recorded: recorded.size,
103+
detected: size,
93104
});
94105
}
95106
})
96-
.or_insert_with(|| (size, path.to_path_buf().pipe(LinkPathList::single)));
107+
.or_insert_with(|| {
108+
let paths = path.to_path_buf().pipe(LinkPathList::single);
109+
Value { size, links, paths }
110+
});
97111
size_assertion.map_err(AddError::SizeConflict)
98112
}
99113
}

src/hardlink/hardlink_list/iter.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::HardlinkList;
1+
use super::{HardlinkList, Value};
22
use crate::{hardlink::LinkPathList, inode::InodeNumber};
33
use dashmap::{iter::Iter as DashIter, mapref::multiple::RefMulti};
44
use pipe_trait::Pipe;
@@ -7,7 +7,7 @@ use pipe_trait::Pipe;
77
#[derive(derive_more::Debug)]
88
#[debug(bound())]
99
#[debug("Iter(..)")]
10-
pub struct Iter<'a, Size>(DashIter<'a, InodeNumber, (Size, LinkPathList)>);
10+
pub struct Iter<'a, Size>(DashIter<'a, InodeNumber, Value<Size>>);
1111

1212
impl<Size> HardlinkList<Size> {
1313
/// Iterate over the recorded entries.
@@ -20,7 +20,7 @@ impl<Size> HardlinkList<Size> {
2020
#[derive(derive_more::Debug)]
2121
#[debug(bound())]
2222
#[debug("Item(..)")]
23-
pub struct Item<'a, Size>(RefMulti<'a, InodeNumber, (Size, LinkPathList)>);
23+
pub struct Item<'a, Size>(RefMulti<'a, InodeNumber, Value<Size>>);
2424

2525
impl<'a, Size> Iterator for Iter<'a, Size> {
2626
type Item = Item<'a, Size>;
@@ -30,18 +30,23 @@ impl<'a, Size> Iterator for Iter<'a, Size> {
3030
}
3131

3232
impl<'a, Size> Item<'a, Size> {
33-
/// Number of the inode.
33+
/// The inode number of the file.
3434
pub fn ino(&self) -> InodeNumber {
3535
*self.0.key()
3636
}
3737

38-
/// Size of the inode.
38+
/// Size of the file.
3939
pub fn size(&self) -> &Size {
40-
&self.0.value().0
40+
&self.0.value().size
4141
}
4242

43-
/// Links of the inode.
44-
pub fn links(&self) -> &LinkPathList {
45-
&self.0.value().1
43+
/// Total number of links of the file, both listed (in [`Self::paths`]) and unlisted.
44+
pub fn links(&self) -> u64 {
45+
self.0.value().links
46+
}
47+
48+
/// Paths to the detected links of the file.
49+
pub fn paths(&self) -> &LinkPathList {
50+
&self.0.value().paths
4651
}
4752
}

src/hardlink/hardlink_list/reflection.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
use super::HardlinkList;
2-
use crate::{
3-
hardlink::{LinkPathList, LinkPathListReflection},
4-
inode::InodeNumber,
5-
};
1+
use super::{HardlinkList, Value};
2+
use crate::{hardlink::LinkPathListReflection, inode::InodeNumber};
63
use dashmap::DashMap;
74
use derive_more::{Display, Error, Into, IntoIterator};
85
use pipe_trait::Pipe;
@@ -44,16 +41,38 @@ impl<Size> Reflection<Size> {
4441
#[derive(Debug, Clone, PartialEq, Eq)]
4542
#[cfg_attr(feature = "json", derive(Deserialize, Serialize))]
4643
pub struct ReflectionEntry<Size> {
44+
/// The inode number of the file.
4745
pub ino: InodeNumber,
46+
/// Size of the file.
4847
pub size: Size,
49-
pub links: LinkPathListReflection,
48+
/// Total number of links of the file, both listed (in [`Self::paths`]) and unlisted.
49+
pub links: u64,
50+
/// Paths to the detected links of the file.
51+
pub paths: LinkPathListReflection,
5052
}
5153

5254
impl<Size> ReflectionEntry<Size> {
53-
/// Create a new value.
54-
fn new(ino: InodeNumber, size: Size, links: LinkPathList) -> Self {
55-
let links = links.into();
56-
ReflectionEntry { ino, size, links }
55+
/// Create a new entry.
56+
fn new(ino: InodeNumber, Value { size, links, paths }: Value<Size>) -> Self {
57+
let paths = paths.into();
58+
ReflectionEntry {
59+
ino,
60+
size,
61+
links,
62+
paths,
63+
}
64+
}
65+
66+
/// Dissolve [`ReflectionEntry`] into a pair of [`InodeNumber`] and [`Value`].
67+
fn dissolve(self) -> (InodeNumber, Value<Size>) {
68+
let ReflectionEntry {
69+
ino,
70+
size,
71+
links,
72+
paths,
73+
} = self;
74+
let paths = paths.into();
75+
(ino, Value { size, links, paths })
5776
}
5877
}
5978

@@ -68,7 +87,7 @@ impl<Size> From<Vec<ReflectionEntry<Size>>> for Reflection<Size> {
6887
impl<Size> From<HardlinkList<Size>> for Reflection<Size> {
6988
fn from(HardlinkList(list): HardlinkList<Size>) -> Self {
7089
list.into_iter()
71-
.map(|(ino, (size, links))| ReflectionEntry::new(ino, size, links))
90+
.map(|(ino, value)| ReflectionEntry::new(ino, value))
7291
.collect::<Vec<_>>()
7392
.pipe(Reflection::from)
7493
}
@@ -89,9 +108,9 @@ impl<Size> TryFrom<Reflection<Size>> for HardlinkList<Size> {
89108
fn try_from(Reflection(entries): Reflection<Size>) -> Result<Self, Self::Error> {
90109
let map = DashMap::with_capacity(entries.len());
91110

92-
for ReflectionEntry { ino, size, links } in entries {
93-
let links = links.into();
94-
if map.insert(ino, (size, links)).is_some() {
111+
for entry in entries {
112+
let (ino, value) = entry.dissolve();
113+
if map.insert(ino, value).is_some() {
95114
return ino.pipe(ConversionError::DuplicatedInode).pipe(Err);
96115
}
97116
}

src/hardlink/hardlink_list/summary.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ impl<Size: Copy> From<ReflectionEntry<Size>> for SingleInodeSummary<Size> {
146146
impl<'r, Size: Copy> From<&'r ReflectionEntry<Size>> for SingleInodeSummary<Size> {
147147
fn from(reflection: &'r ReflectionEntry<Size>) -> Self {
148148
SingleInodeSummary {
149-
links: reflection.links.len(),
149+
links: reflection.paths.len(),
150150
size: reflection.size,
151151
}
152152
}
@@ -161,7 +161,7 @@ impl<'a, Size: Copy> From<IterItem<'a, Size>> for SingleInodeSummary<Size> {
161161
impl<'r, 'a, Size: Copy> From<&'r IterItem<'a, Size>> for SingleInodeSummary<Size> {
162162
fn from(value: &'r IterItem<'a, Size>) -> Self {
163163
SingleInodeSummary {
164-
links: value.links().len(),
164+
links: value.paths().len(),
165165
size: *value.size(),
166166
}
167167
}

0 commit comments

Comments
 (0)