file_engine/analysis/progress.rs
1use std::path::PathBuf;
2
3use tokio::sync::mpsc;
4
5/// Deliberately a separate, smaller enum from `crate::progress::Progress`
6/// rather than a reuse: `Progress` (and the `Handle<T>` it's threaded
7/// through) live behind the `operations` feature, which `analyze`
8/// doesn't require — see `AnalysisHandle`'s doc comment for the same
9/// reasoning applied one level up.
10///
11/// `#[non_exhaustive]` for the same reason as `Progress`: adding a
12/// variant later shouldn't be a breaking change for an exhaustive match.
13#[derive(Debug, Clone)]
14#[non_exhaustive]
15pub enum AnalysisProgress {
16 /// Always emitted first, exactly once. `estimated_entries` is `Some`
17 /// only when `AnalyzeBuilder::estimate_total(true)` was set — getting
18 /// a count requires a full extra pass over the tree (stat-ing every
19 /// file against the same filters as the real walk), so it's `None`
20 /// by default rather than paying that cost unconditionally.
21 Started { estimated_entries: Option<usize> },
22 /// Emitted once per matched entry (after filters, not per entry
23 /// walked) as the tree is scanned.
24 EntryAnalyzed { path: PathBuf },
25 /// Emitted once per file as it finishes content-hashing during
26 /// duplicate detection — the slow phase, so this is the only signal
27 /// of liveness while it runs. Only emitted when
28 /// `.detect_duplicates(true)` is set.
29 #[cfg(feature = "checksum")]
30 EntryHashed { path: PathBuf },
31}
32
33#[derive(Clone)]
34pub(crate) struct AnalysisProgressReporter {
35 tx: mpsc::UnboundedSender<AnalysisProgress>,
36}
37
38impl AnalysisProgressReporter {
39 pub(crate) fn new(tx: mpsc::UnboundedSender<AnalysisProgress>) -> Self {
40 Self { tx }
41 }
42
43 /// A closed receiver means nobody's listening — not a failure, just
44 /// nothing to report to. Mirrors `ProgressReporter::send`.
45 pub(crate) fn send(&self, progress: AnalysisProgress) {
46 let _ = self.tx.send(progress);
47 }
48}