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::{device::DeviceNumber, 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(Clone, Copy, PartialEq, Eq, Hash, Debug)]
25struct InodeKey {
26 ino: InodeNumber,
28 dev: DeviceNumber,
30}
31
32#[derive(Debug, Clone)]
34struct Value<Size> {
35 size: Size,
37 links: u64,
39 paths: LinkPathList,
41}
42
43#[derive(Debug, SmartDefault, Clone)]
49pub struct HardlinkList<Size>(
50 DashMap<InodeKey, Value<Size>>,
52);
53
54impl<Size> HardlinkList<Size> {
55 pub fn new() -> Self {
57 HardlinkList::default()
58 }
59
60 pub fn len(&self) -> usize {
62 self.0.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.0.is_empty()
68 }
69
70 pub fn into_reflection(self) -> Reflection<Size> {
72 self.into()
73 }
74}
75
76#[derive(Debug, Display, Error)]
83#[cfg_attr(test, derive(PartialEq, Eq))]
84#[display(bound(Size: Debug))]
85#[display("Size for inode {ino} on device {dev} changed from {recorded:?} to {detected:?}")]
86pub struct SizeConflictError<Size> {
87 pub ino: InodeNumber,
88 pub dev: DeviceNumber,
89 pub recorded: Size,
90 pub detected: Size,
91}
92
93#[derive(Debug, Display, Error)]
102#[cfg_attr(test, derive(PartialEq, Eq))]
103#[display(
104 "Number of links of inode {ino} on device {dev} changed from {recorded:?} to {detected:?}"
105)]
106pub struct NumberOfLinksConflictError {
107 pub ino: InodeNumber,
108 pub dev: DeviceNumber,
109 pub recorded: u64,
110 pub detected: u64,
111}
112
113#[derive(Debug, Display, Error)]
115#[cfg_attr(test, derive(PartialEq, Eq))]
116#[display(bound(Size: Debug))]
117#[non_exhaustive]
118pub enum AddError<Size> {
119 SizeConflict(SizeConflictError<Size>),
120 NumberOfLinksConflict(NumberOfLinksConflictError),
121}
122
123impl<Size> HardlinkList<Size>
124where
125 Size: size::Size,
126{
127 #[cfg(any(unix, test))] pub(crate) fn add(
130 &self,
131 ino: InodeNumber,
132 dev: DeviceNumber,
133 size: Size,
134 links: u64,
135 path: &Path,
136 ) -> Result<(), AddError<Size>> {
137 let key = InodeKey { ino, dev };
138 let mut assertions = Ok(());
139 self.0
140 .entry(key)
141 .and_modify(|recorded| {
142 if size != recorded.size {
143 assertions = Err(AddError::SizeConflict(SizeConflictError {
144 ino,
145 dev,
146 recorded: recorded.size,
147 detected: size,
148 }));
149 return;
150 }
151
152 if links != recorded.links {
153 assertions = Err(AddError::NumberOfLinksConflict(
154 NumberOfLinksConflictError {
155 ino,
156 dev,
157 recorded: recorded.links,
158 detected: links,
159 },
160 ));
161 return;
162 }
163
164 recorded.paths.add(path.to_path_buf());
165 })
166 .or_insert_with(|| {
167 let paths = path.to_path_buf().pipe(LinkPathList::single);
168 Value { size, links, paths }
169 });
170 assertions
171 }
172}
173
174#[cfg(test)]
175mod test;