Skip to main content

weavatrix_scan/
content_visit.rs

1use crate::report::{
2    CompactScanReport, CompactScannedFile, IgnoreSourceEvidence, ScanCacheStats, ScanTermination,
3    ScanWarning, SkippedEntry,
4};
5use std::path::{Path, PathBuf};
6
7/// Controls whether a content visit retains selected-file evidence internally.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ContentVisitMode {
10    /// Retain compact selected-file evidence and compute a deterministic
11    /// revision.
12    Revision,
13    /// Emit bytes and counters without retaining selected-file evidence or
14    /// computing a revision.
15    Streaming,
16}
17
18/// Controls delivery from [`crate::Scanner::visit_content`].
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum ContentVisitControl {
21    /// Continue delivering the current file.
22    Continue,
23    /// Stop delivering chunks for this file while allowing required scanner
24    /// evidence work to finish.
25    SkipFile,
26    /// Cooperatively stop every content worker.
27    Quit,
28}
29
30/// Stable identity and discovery evidence for one selected file.
31#[derive(Debug, Clone, Copy)]
32pub struct ContentFile<'a> {
33    /// Root insertion index. A single-root [`crate::Scanner`] always uses zero.
34    pub root_index: usize,
35    /// Monotonic work sequence within this visit. Sort by `root_index` and
36    /// `relative` when results must be deterministic across runs.
37    pub sequence: u64,
38    pub root: &'a Path,
39    pub absolute: &'a Path,
40    pub relative: &'a str,
41    pub bytes: u64,
42}
43
44/// Result of verifying the file around its single content read.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum ContentFileStatus {
47    Selected,
48    Binary,
49    Changed,
50}
51
52/// Events emitted from one bounded content worker.
53#[derive(Debug)]
54pub enum ContentVisitEvent<'a> {
55    /// The selected file was opened and its discovery evidence was verified.
56    FileStart {
57        worker_index: usize,
58        file: ContentFile<'a>,
59    },
60    /// A borrowed chunk from the scanner's one content read.
61    Chunk {
62        worker_index: usize,
63        file: ContentFile<'a>,
64        offset: u64,
65        bytes: &'a [u8],
66    },
67    /// The file read and post-read verification finished.
68    FileEnd {
69        worker_index: usize,
70        file: ContentFile<'a>,
71        status: ContentFileStatus,
72        bytes_read: u64,
73        content_hash: Option<&'a str>,
74        consumer_skipped: bool,
75    },
76}
77
78/// Summary of a parallel, one-pass selected-content visit.
79#[derive(Debug)]
80pub struct ContentVisitReport {
81    pub mode: ContentVisitMode,
82    pub root: PathBuf,
83    /// Deterministic selected-file manifest retained in revision mode.
84    ///
85    /// Every entry carries the content hash and file-version evidence produced
86    /// by the same read that emitted its bytes to the visitor. Streaming mode
87    /// leaves this empty.
88    pub files: Vec<CompactScannedFile>,
89    /// Candidates selected by traversal and ignore rules before content checks.
90    pub discovered: u64,
91    /// Files that completed content checks and remain selected.
92    pub completed: u64,
93    pub opened: u64,
94    pub chunks: u64,
95    pub bytes_read: u64,
96    pub bytes_emitted: u64,
97    pub consumer_skipped: u64,
98    pub stopped: bool,
99    pub skipped: Vec<SkippedEntry>,
100    pub warnings: Vec<ScanWarning>,
101    pub ignore_sources: Vec<IgnoreSourceEvidence>,
102    pub revision: String,
103    pub complete: bool,
104    pub termination: Option<ScanTermination>,
105    pub portable: bool,
106    pub cache: ScanCacheStats,
107}
108
109impl ContentVisitReport {
110    /// Converts a revision-mode visit into the compact scanner manifest used
111    /// by incremental consumers.
112    ///
113    /// Streaming visits deliberately do not retain file evidence and therefore
114    /// cannot be converted.
115    ///
116    /// # Errors
117    ///
118    /// Returns an error when called for a streaming visit.
119    pub fn into_compact_scan_report(self) -> Result<CompactScanReport, &'static str> {
120        if self.mode != ContentVisitMode::Revision {
121            return Err("streaming content visits do not retain a file manifest");
122        }
123        Ok(CompactScanReport {
124            root: self.root,
125            files: self.files,
126            skipped: self.skipped,
127            warnings: self.warnings,
128            ignore_sources: self.ignore_sources,
129            revision: self.revision,
130            complete: self.complete,
131            termination: self.termination,
132            portable: self.portable,
133            cache: self.cache,
134        })
135    }
136}
137
138/// Ordered summaries from a multi-root content visit.
139#[derive(Debug)]
140pub struct MultiContentVisitReport {
141    /// Reports remain in the same order as roots were added.
142    pub reports: Vec<ContentVisitReport>,
143}
144
145impl MultiContentVisitReport {
146    #[must_use]
147    pub const fn len(&self) -> usize {
148        self.reports.len()
149    }
150
151    #[must_use]
152    pub const fn is_empty(&self) -> bool {
153        self.reports.is_empty()
154    }
155}
156
157/// Content and removals produced by a safe file-only watcher plan.
158#[derive(Debug)]
159pub struct ChangedContentVisitReport {
160    /// Evidence for the changed files that still exist and remain selected.
161    ///
162    /// Its revision describes this changed-file subset, not the complete
163    /// repository manifest.
164    pub content: ContentVisitReport,
165    /// Stable normalized paths that disappeared from the repository.
166    pub removed: Vec<String>,
167}
168
169/// Result of attempting a traversal-free watcher content visit.
170#[derive(Debug)]
171pub enum ChangedContentVisitOutcome {
172    /// Only changed file paths were matched, opened, and visited.
173    Visited(Box<ChangedContentVisitReport>),
174    /// The plan can affect directory structure or selection and therefore
175    /// requires the caller to perform a complete scan.
176    FullRescanRequired,
177}