parallel_disk_usage/hardlink/
hardlink_list.rs1pub mod iter;
2pub mod reflection;
3pub mod summary;
4
5pub use iter::Iter;
6pub use reflection::Reflection;
7pub use summary::Summary;
8
9pub use Reflection as HardlinkListReflection;
10pub use Summary as SharedLinkSummary;
11
12use crate::{hardlink::LinkPathList, inode::InodeNumber, size};
13use dashmap::DashMap;
14use derive_more::{Display, Error};
15use smart_default::SmartDefault;
16use std::fmt::Debug;
17
18#[cfg(any(unix, test))]
19use pipe_trait::Pipe;
20#[cfg(any(unix, test))]
21use std::path::Path;
22
23#[derive(Debug, Clone)]
25struct Value<Size> {
26 size: Size,
28 links: u64,
30 paths: LinkPathList,
32}
33
34#[derive(Debug, SmartDefault, Clone)]
40pub struct HardlinkList<Size>(
41 DashMap<InodeNumber, Value<Size>>,
43);
44
45impl<Size> HardlinkList<Size> {
46 pub fn new() -> Self {
48 HardlinkList::default()
49 }
50
51 pub fn len(&self) -> usize {
53 self.0.len()
54 }
55
56 pub fn is_empty(&self) -> bool {
58 self.0.is_empty()
59 }
60
61 pub fn into_reflection(self) -> Reflection<Size> {
63 self.into()
64 }
65}
66
67#[derive(Debug, Display, Error)]
72#[cfg_attr(test, derive(PartialEq, Eq))]
73#[display(bound(Size: Debug))]
74#[display("Size for inode {ino} changed from {recorded:?} to {detected:?}")]
75pub struct SizeConflictError<Size> {
76 pub ino: InodeNumber,
77 pub recorded: Size,
78 pub detected: Size,
79}
80
81#[derive(Debug, Display, Error)]
88#[cfg_attr(test, derive(PartialEq, Eq))]
89#[display("Number of links of inode {ino} changed from {recorded:?} to {detected:?}")]
90pub struct NumberOfLinksConflictError {
91 pub ino: InodeNumber,
92 pub recorded: u64,
93 pub detected: u64,
94}
95
96#[derive(Debug, Display, Error)]
98#[cfg_attr(test, derive(PartialEq, Eq))]
99#[display(bound(Size: Debug))]
100#[non_exhaustive]
101pub enum AddError<Size> {
102 SizeConflict(SizeConflictError<Size>),
103 NumberOfLinksConflict(NumberOfLinksConflictError),
104}
105
106impl<Size> HardlinkList<Size>
107where
108 Size: size::Size,
109{
110 #[cfg(any(unix, test))] pub(crate) fn add(
113 &self,
114 ino: InodeNumber,
115 size: Size,
116 links: u64,
117 path: &Path,
118 ) -> Result<(), AddError<Size>> {
119 let mut assertions = Ok(());
120 self.0
121 .entry(ino)
122 .and_modify(|recorded| {
123 if size != recorded.size {
124 assertions = Err(AddError::SizeConflict(SizeConflictError {
125 ino,
126 recorded: recorded.size,
127 detected: size,
128 }));
129 return;
130 }
131
132 if links != recorded.links {
133 assertions = Err(AddError::NumberOfLinksConflict(
134 NumberOfLinksConflictError {
135 ino,
136 recorded: recorded.links,
137 detected: links,
138 },
139 ));
140 return;
141 }
142
143 recorded.paths.add(path.to_path_buf());
144 })
145 .or_insert_with(|| {
146 let paths = path.to_path_buf().pipe(LinkPathList::single);
147 Value { size, links, paths }
148 });
149 assertions
150 }
151}
152
153#[cfg(test)]
154mod test;