Skip to main content

weavatrix_scan/
content_visit.rs

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