Skip to content

Commit f679ea6

Browse files
committed
feat(api)!: remove hook
1 parent e7416d5 commit f679ea6

15 files changed

Lines changed: 191 additions & 170 deletions

src/app.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
args::{Args, Quantity, Threads},
77
bytes_format::BytesFormat,
88
get_size::{GetApparentSize, GetSize},
9-
hook,
9+
hardlink,
1010
json_data::{JsonData, JsonDataBody},
1111
reporter::{ErrorOnlyReporter, ErrorReport, ProgressAndErrorReporter, ProgressReport},
1212
runtime_error::RuntimeError,
@@ -195,13 +195,13 @@ impl App {
195195
const REPORT_PROGRESS: bool,
196196
>: CreateReporter<REPORT_PROGRESS>
197197
{
198-
type Hook: hook::Hook<Self::Size, Self::Reporter>
198+
type HardlinksHandler: hardlink::RecordHardlinks<Self::Size, Self::Reporter>
199199
+ sub::DeduplicateHardlinkSizes<Self::Size>;
200-
fn create_hook(
201-
record: <Self::Hook as sub::DeduplicateHardlinkSizes<Self::Size>>::HardlinkRecord,
202-
) -> Self::Hook;
200+
fn create_hardlinks_handler(
201+
record: <Self::HardlinksHandler as sub::DeduplicateHardlinkSizes<Self::Size>>::HardlinkRecord,
202+
) -> Self::HardlinksHandler;
203203
fn init_hardlink_record(
204-
) -> <Self::Hook as sub::DeduplicateHardlinkSizes<Self::Size>>::HardlinkRecord;
204+
) -> <Self::HardlinksHandler as sub::DeduplicateHardlinkSizes<Self::Size>>::HardlinkRecord;
205205
}
206206

207207
impl<const REPORT_PROGRESS: bool, SizeGetter>
@@ -210,9 +210,9 @@ impl App {
210210
SizeGetter: CreateReporter<REPORT_PROGRESS>,
211211
SizeGetter::Size: Send + Sync,
212212
{
213-
type Hook = hook::DoNothing;
214-
fn create_hook((): ()) -> Self::Hook {
215-
hook::DoNothing
213+
type HardlinksHandler = hardlink::HardlinkIgnorant;
214+
fn create_hardlinks_handler((): ()) -> Self::HardlinksHandler {
215+
hardlink::HardlinkIgnorant
216216
}
217217
fn init_hardlink_record() {}
218218
}
@@ -225,9 +225,11 @@ impl App {
225225
SizeGetter::Size: Send + Sync + 'static,
226226
SizeGetter::Reporter: crate::reporter::Reporter<SizeGetter::Size>,
227227
{
228-
type Hook = hook::RecordHardlink<'static, Self::Size>;
229-
fn create_hook(record: &'static HardlinkList<Self::Size>) -> Self::Hook {
230-
hook::RecordHardlink::new(record)
228+
type HardlinksHandler = hardlink::HardlinkAware<'static, Self::Size>;
229+
fn create_hardlinks_handler(
230+
record: &'static HardlinkList<Self::Size>,
231+
) -> Self::HardlinksHandler {
232+
hardlink::HardlinkAware::new(record)
231233
}
232234
fn init_hardlink_record() -> &'static HardlinkList<Self::Size> {
233235
HardlinkList::new().pipe(Box::new).pipe(Box::leak)
@@ -257,13 +259,15 @@ impl App {
257259
} => {
258260
const DEDUPLICATE_HARDLINKS: bool = cfg!(unix) && $deduplicate_hardlinks;
259261
let hardlink_record = <$size_getter as HardlinkDeduplicationSystem<DEDUPLICATE_HARDLINKS, $progress>>::init_hardlink_record();
260-
let hook = <$size_getter as HardlinkDeduplicationSystem<DEDUPLICATE_HARDLINKS, $progress>>::create_hook(hardlink_record);
262+
let hardlinks_handler = <
263+
$size_getter as HardlinkDeduplicationSystem<DEDUPLICATE_HARDLINKS, $progress>
264+
>::create_hardlinks_handler(hardlink_record);
261265

262266
Sub {
263267
direction: Direction::from_top_down(top_down),
264268
bar_alignment: BarAlignment::from_align_right(align_right),
265269
size_getter: <$size_getter as GetSizeUtils>::INSTANCE,
266-
hook,
270+
hardlinks_handler,
267271
hardlink_record,
268272
reporter: <$size_getter as CreateReporter<$progress>>::create_reporter(report_error),
269273
bytes_format: <$size_getter as GetSizeUtils>::formatter(bytes_format),

src/app/sub.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use crate::{
33
data_tree::DataTree,
44
fs_tree_builder::FsTreeBuilder,
55
get_size::GetSize,
6-
hardlink::HardlinkListReflection,
7-
hook,
6+
hardlink::{HardlinkIgnorant, HardlinkListReflection, RecordHardlinks},
87
json_data::{BinaryVersion, JsonData, JsonDataBody, JsonTree, SchemaVersion},
98
os_string_display::OsStringDisplay,
109
reporter::ParallelReporter,
@@ -18,12 +17,12 @@ use serde::Serialize;
1817
use std::{io::stdout, iter::once, num::NonZeroU64, path::PathBuf};
1918

2019
/// The sub program of the main application.
21-
pub struct Sub<Size, SizeGetter, Hook, Report>
20+
pub struct Sub<Size, SizeGetter, HardlinksHandler, Report>
2221
where
2322
Report: ParallelReporter<Size> + Sync,
2423
Size: size::Size + Into<u64> + Serialize + Send + Sync,
2524
SizeGetter: GetSize<Size = Size> + Copy + Sync,
26-
Hook: hook::Hook<Size, Report> + DeduplicateHardlinkSizes<Size> + Copy + Sync,
25+
HardlinksHandler: RecordHardlinks<Size, Report> + DeduplicateHardlinkSizes<Size> + Copy + Sync,
2726
JsonTree<Size>: Into<JsonDataBody>,
2827
{
2928
/// List of files and/or directories.
@@ -42,10 +41,10 @@ where
4241
pub max_depth: NonZeroU64,
4342
/// [Get the size](GetSize) of files/directories.
4443
pub size_getter: SizeGetter,
45-
/// Hook to run after [`Self::size_getter`].
46-
pub hook: Hook,
44+
/// Handle to detect, record, and deduplicate hardlinks.
45+
pub hardlinks_handler: HardlinksHandler,
4746
/// Record of detected hardlinks.
48-
pub hardlink_record: Hook::HardlinkRecord,
47+
pub hardlink_record: HardlinksHandler::HardlinkRecord,
4948
/// Reports measurement progress.
5049
pub reporter: Report,
5150
/// Minimal size proportion required to appear.
@@ -54,12 +53,12 @@ where
5453
pub no_sort: bool,
5554
}
5655

57-
impl<Size, SizeGetter, Hook, Report> Sub<Size, SizeGetter, Hook, Report>
56+
impl<Size, SizeGetter, HardlinksHandler, Report> Sub<Size, SizeGetter, HardlinksHandler, Report>
5857
where
5958
Size: size::Size + Into<u64> + Serialize + Send + Sync,
6059
Report: ParallelReporter<Size> + Sync,
6160
SizeGetter: GetSize<Size = Size> + Copy + Sync,
62-
Hook: hook::Hook<Size, Report> + DeduplicateHardlinkSizes<Size> + Copy + Sync,
61+
HardlinksHandler: RecordHardlinks<Size, Report> + DeduplicateHardlinkSizes<Size> + Copy + Sync,
6362
JsonTree<Size>: Into<JsonDataBody>,
6463
{
6564
/// Run the sub program.
@@ -73,7 +72,7 @@ where
7372
column_width_distribution,
7473
max_depth,
7574
size_getter,
76-
hook,
75+
hardlinks_handler,
7776
hardlink_record,
7877
reporter,
7978
min_ratio,
@@ -89,7 +88,7 @@ where
8988
reporter: &reporter,
9089
root,
9190
size_getter,
92-
hook,
91+
hardlinks_recorder: hardlinks_handler,
9392
max_depth,
9493
}
9594
.into()
@@ -100,7 +99,7 @@ where
10099
} else {
101100
return Sub {
102101
files: vec![".".into()],
103-
hook,
102+
hardlinks_handler,
104103
hardlink_record,
105104
reporter,
106105
..self
@@ -135,7 +134,7 @@ where
135134
data_tree.par_sort_by(|left, right| left.size().cmp(&right.size()).reverse());
136135
}
137136
let deduplication_record =
138-
Hook::deduplicate_hardlink_sizes(&mut data_tree, hardlink_record);
137+
HardlinksHandler::deduplicate_hardlink_sizes(&mut data_tree, hardlink_record);
139138
(data_tree, deduplication_record)
140139
};
141140

@@ -146,7 +145,8 @@ where
146145
.into_reflection() // I really want to use std::mem::transmute here but can't.
147146
.par_convert_names_to_utf8() // TODO: allow non-UTF8 somehow.
148147
.expect("convert all names from raw string to UTF-8");
149-
let shared = deduplication_record?.pipe(Hook::reflect_deduplication_results)?;
148+
let shared =
149+
deduplication_record?.pipe(HardlinksHandler::reflect_deduplication_results)?;
150150
let json_tree = JsonTree { tree, shared };
151151
let json_data = JsonData {
152152
schema_version: SchemaVersion,
@@ -166,7 +166,7 @@ where
166166
};
167167

168168
print!("{visualizer}"); // visualizer already ends with "\n", println! isn't needed here.
169-
Hook::report_deduplication_results(deduplication_record?, bytes_format)?;
169+
HardlinksHandler::report_deduplication_results(deduplication_record?, bytes_format)?;
170170
Ok(())
171171
}
172172
}
@@ -194,7 +194,7 @@ pub trait DeduplicateHardlinkSizes<Size: size::Size> {
194194
}
195195

196196
#[cfg(unix)]
197-
impl<'a, Size> DeduplicateHardlinkSizes<Size> for hook::RecordHardlink<'a, Size>
197+
impl<'a, Size> DeduplicateHardlinkSizes<Size> for crate::hardlink::HardlinkAware<'a, Size>
198198
where
199199
DataTree<OsStringDisplay, Size>: Send,
200200
Size: size::Size + Sync,
@@ -256,7 +256,7 @@ where
256256
}
257257
}
258258

259-
impl<Size> DeduplicateHardlinkSizes<Size> for hook::DoNothing
259+
impl<Size> DeduplicateHardlinkSizes<Size> for HardlinkIgnorant
260260
where
261261
DataTree<OsStringDisplay, Size>: Send,
262262
Size: size::Size + Sync,

src/fs_tree_builder.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{
22
data_tree::DataTree,
33
get_size::GetSize,
4-
hook::{self, HookArgument},
4+
hardlink::record::{RecordHardlinks, RecordHardlinksArgument},
55
os_string_display::OsStringDisplay,
66
reporter::{error_report::Operation::*, ErrorReport, Event, Reporter},
77
size,
@@ -25,51 +25,52 @@ use std::{
2525
/// os_string_display::OsStringDisplay,
2626
/// reporter::{ErrorOnlyReporter, ErrorReport},
2727
/// size::Bytes,
28-
/// hook,
28+
/// hardlink::HardlinkIgnorant,
2929
/// };
3030
/// let builder = FsTreeBuilder {
3131
/// root: std::env::current_dir().unwrap(),
32-
/// hook: hook::DoNothing,
32+
/// hardlinks_recorder: HardlinkIgnorant,
3333
/// size_getter: GetApparentSize,
3434
/// reporter: &ErrorOnlyReporter::new(ErrorReport::SILENT),
3535
/// max_depth: 10,
3636
/// };
3737
/// let data_tree: DataTree<OsStringDisplay, Bytes> = builder.into();
3838
/// ```
3939
#[derive(Debug)]
40-
pub struct FsTreeBuilder<'a, Size, SizeGetter, Hook, Report>
40+
pub struct FsTreeBuilder<'a, Size, SizeGetter, HardlinksRecorder, Report>
4141
where
4242
Report: Reporter<Size> + Sync + ?Sized,
4343
Size: size::Size + Send + Sync,
4444
SizeGetter: GetSize<Size = Size> + Sync,
45-
Hook: hook::Hook<Size, Report> + Sync,
45+
HardlinksRecorder: RecordHardlinks<Size, Report> + Sync,
4646
{
4747
/// Root of the directory tree.
4848
pub root: PathBuf,
4949
/// Returns size of an item.
5050
pub size_getter: SizeGetter,
51-
/// Hook to run after [`Self::size_getter`].
52-
pub hook: Hook,
51+
/// Handle to detect and record hardlinks.
52+
pub hardlinks_recorder: HardlinksRecorder,
5353
/// Reports progress to external system.
5454
pub reporter: &'a Report,
5555
/// Deepest level of descendent display in the graph. The sizes beyond the max depth still count toward total.
5656
pub max_depth: u64,
5757
}
5858

59-
impl<'a, Size, SizeGetter, Hook, Report> From<FsTreeBuilder<'a, Size, SizeGetter, Hook, Report>>
59+
impl<'a, Size, SizeGetter, HardlinksRecorder, Report>
60+
From<FsTreeBuilder<'a, Size, SizeGetter, HardlinksRecorder, Report>>
6061
for DataTree<OsStringDisplay, Size>
6162
where
6263
Report: Reporter<Size> + Sync + ?Sized,
6364
Size: size::Size + Send + Sync,
6465
SizeGetter: GetSize<Size = Size> + Sync,
65-
Hook: hook::Hook<Size, Report> + Sync,
66+
HardlinksRecorder: RecordHardlinks<Size, Report> + Sync,
6667
{
6768
/// Create a [`DataTree`] from an [`FsTreeBuilder`].
68-
fn from(builder: FsTreeBuilder<Size, SizeGetter, Hook, Report>) -> Self {
69+
fn from(builder: FsTreeBuilder<Size, SizeGetter, HardlinksRecorder, Report>) -> Self {
6970
let FsTreeBuilder {
7071
root,
7172
size_getter,
72-
hook,
73+
hardlinks_recorder,
7374
reporter,
7475
max_depth,
7576
} = builder;
@@ -97,7 +98,9 @@ where
9798
let is_dir = stats.is_dir();
9899
let size = size_getter.get_size(&stats);
99100
reporter.report(Event::ReceiveData(size));
100-
hook.run_hook(HookArgument::new(path, &stats, size, reporter));
101+
hardlinks_recorder.record_hardlinks(RecordHardlinksArgument::new(
102+
path, &stats, size, reporter,
103+
));
101104
(is_dir, size)
102105
}
103106
};

src/hardlink.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1+
mod ignorant;
2+
pub use ignorant::HardlinkIgnorant;
3+
4+
// `RecordHardlink` is POSIX-exclusive, because whilst Windows does have `MetadataExt::number_of_links`, it requires Nightly.
5+
#[cfg(unix)]
6+
mod aware;
7+
#[cfg(unix)]
8+
pub use aware::HardlinkAware;
9+
110
pub mod hardlink_list;
211
pub mod link_path_list;
12+
pub mod record;
313

414
pub use hardlink_list::{HardlinkList, HardlinkListReflection};
515
pub use link_path_list::{LinkPathList, LinkPathListReflection};
16+
pub use record::RecordHardlinks;

src/hardlink/aware.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use super::record::{RecordHardlinks, RecordHardlinksArgument};
2+
use crate::{
3+
hardlink::HardlinkList,
4+
inode::InodeNumber,
5+
reporter::{event::HardlinkDetection, Event, Reporter},
6+
size,
7+
};
8+
use std::{fmt::Debug, os::unix::fs::MetadataExt};
9+
10+
/// Be aware of hardlinks. Treat them as links that share space.
11+
/// Detect files with more than 1 links and record them.
12+
/// Deduplicate them (remove duplicated size) from total size to
13+
/// accurately reflect the real size of their containers.
14+
#[derive(Debug, Clone, Copy)]
15+
pub struct HardlinkAware<'a, Size> {
16+
/// Map an inode number to its size and detected paths.
17+
record: &'a HardlinkList<Size>,
18+
}
19+
20+
impl<'a, Size> HardlinkAware<'a, Size> {
21+
/// Create a detector/recorder of hardlinks.
22+
pub fn new(record: &'a HardlinkList<Size>) -> Self {
23+
HardlinkAware { record }
24+
}
25+
}
26+
27+
impl<'a, Size, Report> RecordHardlinks<Size, Report> for HardlinkAware<'a, Size>
28+
where
29+
Size: size::Size + Eq + Debug,
30+
Report: Reporter<Size> + ?Sized,
31+
{
32+
fn record_hardlinks(&self, argument: RecordHardlinksArgument<Size, Report>) {
33+
let RecordHardlinksArgument {
34+
path,
35+
stats,
36+
size,
37+
reporter,
38+
} = argument;
39+
40+
if stats.is_dir() {
41+
return;
42+
}
43+
44+
let links = stats.nlink();
45+
if links <= 1 {
46+
return;
47+
}
48+
49+
reporter.report(Event::DetectHardlink(HardlinkDetection {
50+
path,
51+
stats,
52+
size,
53+
links,
54+
}));
55+
56+
let ino = InodeNumber::get(stats);
57+
self.record.add(ino, size, path).unwrap(); // TODO: propagate the error
58+
}
59+
}

src/hardlink/hardlink_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use derive_more::{Display, Error};
1010
use pipe_trait::Pipe;
1111
use std::{fmt::Debug, path::Path};
1212

13-
/// Storage to be used by [`crate::hook::RecordHardlink`].
13+
/// Storage to be used by [`crate::hardlink::RecordHardlinks`].
1414
///
1515
/// **Serialization and deserialization:** _(feature: `json`)_ `HardlinkList` does not implement
1616
/// `Serialize` and `Deserialize` traits directly, instead, it can be converted into/from a

src/hardlink/ignorant.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use super::record::{RecordHardlinks, RecordHardlinksArgument};
2+
3+
/// Be ignorant of hardlinks. Treat them as real files.
4+
/// Do not detect it. Do not deduplicate it.
5+
/// Essentially no-op.
6+
#[derive(Debug, Default, Clone, Copy)]
7+
pub struct HardlinkIgnorant;
8+
9+
/// Do nothing to detect nor record any hardlink.
10+
impl<Size, Reporter> RecordHardlinks<Size, Reporter> for HardlinkIgnorant {
11+
/// Do nothing.
12+
fn record_hardlinks(&self, _: RecordHardlinksArgument<Size, Reporter>) {}
13+
}

0 commit comments

Comments
 (0)