1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
//! What:
//! Scan every eligible candidate of one version through the loaded scanner.
//! Why:
//! This is the whole forbidden-strings check of one policy pass:
//! candidate bytes
//! come from the candidate store (staged or committed,
//! never the worktree),
//! go to
//! the scanner in memory,
//! and the scanner's own results come back.
//! No temporary
//! file is written and no process is started per file.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! // const scans = await scanVersion(scanner, store, version, rulesPath);
//! ```
/// Import the candidate layer's failure,
/// store and version.
use crateCandidateError;
use crateCandidateStore;
use crate;
/// Import the candidates whose content a parent already holds.
use crateKnownContent;
/// Import the loaded scanner and its failure.
use crate;
/// Import the eligibility decision.
use crateis_scannable;
/// The scanner library's per-candidate result,
/// returned unchanged.
use CandidateScan;
/// `Rc<T>` is a shared,
/// read-only handle to one heap value (siblings `Box<T>`,
/// `Arc<T>`).
use Rc;
/// What:
/// Why a scan pass stopped:
/// the candidate layer failed,
/// or the scanner did.
/// Each variant carries the original typed failure.
/// Why:
/// Either way some file was not checked,
/// so the pass has no result.
/// Keeping the
/// original failure lets the caller report its own cause.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// type ScanRunError = { kind: 'candidate'; error: CandidateError } | { kind: 'scanner'; error: ScannerError };
/// ```
/// What:
/// `impl std::fmt::Display for ScanRunError` supplies Rust's "print me" interface.
/// Why:
/// The message is the wrapped failure's own,
/// unchanged.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// toString(): string { return this.error.message; }
/// ```
/// An empty `impl` marks the type as a standard error value.
/// What:
/// Where a scan pass reads candidate bytes.
/// A `trait` is an interface.
/// Why:
/// The candidate store reads the version's objects;
/// a direct fix reads the same
/// objects with its in-memory corrections laid over them,
/// and its later passes
/// must scan the corrected bytes.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// interface CandidateBytes { bytes(candidate: Candidate): Promise<Uint8Array> }
/// ```
/// What:
/// `impl CandidateBytes for CandidateStore` reads through the store itself.
/// Why:
/// Without corrections the store's objects are the bytes to scan.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// class CandidateStore implements CandidateBytes {}
/// ```
/// What:
/// Scan each eligible candidate and return the scanner's result for every one scanned.
/// `&mut dyn CandidateBytes` lends any byte reader for changing (it may read objects);
/// `&[Vec<u8>]` is "the rules files' candidate pathnames, none when no file is tracked";
/// `Result<T, E>` is "a value or a failure".
/// Why:
/// Results are returned for clean candidates too,
/// so the caller can tell
/// "scanned and clean" from "not scanned".
/// The first failure ends the pass with
/// no partial result,
/// because a partial result would look like a clean pass.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// async function scanVersion(scanner, store, version, rulesPath?): Promise<CandidateScan[]>;
/// ```
/// What:
/// `scan_version`,
/// except that a candidate whose content a parent already holds is given to the scanner without
/// bytes.
/// `&KnownContent` lists those candidates by position.
/// Why:
/// The scanner reads the pathname of every candidate it is given,
/// so a known candidate still has its
/// new pathname checked,
/// and its bytes are neither read from the store nor scanned.
/// The result list
/// keeps one entry per eligible candidate,
/// so "scanned" and "skipped" differ only in the bytes behind
/// the entry.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// async function scanVersionSkipping(scanner, store, version, rulesPath, known): Promise<CandidateScan[]>;
/// ```
/// Which candidates a pass skips,
/// and how a pass ends on failure.