1use std::path::PathBuf;
2
3mod compact;
4mod scan;
5
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct FileIdentity {
9 pub file_system: u64,
10 pub file: u64,
11}
12
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
15pub struct FileVersion {
16 #[cfg_attr(feature = "serde", serde(default))]
17 pub modified_ns: Option<u128>,
18 #[cfg_attr(feature = "serde", serde(default))]
19 pub changed_ns: Option<u128>,
20 #[cfg_attr(feature = "serde", serde(default))]
21 pub identity: Option<FileIdentity>,
22}
23
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ScannedFile {
27 #[cfg_attr(feature = "serde", serde(with = "crate::path_serde"))]
28 pub absolute: PathBuf,
29 pub relative: String,
30 pub bytes: u64,
31 pub content_hash: Option<String>,
32 #[cfg_attr(feature = "serde", serde(default))]
34 pub content_fingerprint: Option<String>,
35 #[cfg_attr(feature = "serde", serde(default))]
36 pub version: FileVersion,
37 #[cfg_attr(feature = "serde", serde(default))]
38 pub binary_checked: bool,
39}
40
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct CompactScannedFile {
48 pub relative: Box<str>,
49 pub bytes: u64,
50 #[cfg_attr(feature = "serde", serde(default))]
52 pub content: Option<Box<CompactContentEvidence>>,
53}
54
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct CompactContentEvidence {
59 pub content_hash: Option<Box<str>>,
60 #[cfg_attr(feature = "serde", serde(default))]
62 pub content_fingerprint: Option<Box<str>>,
63 #[cfg_attr(feature = "serde", serde(default))]
64 pub version: FileVersion,
65 #[cfg_attr(feature = "serde", serde(default))]
66 pub binary_checked: bool,
67}
68
69impl CompactScannedFile {
70 #[must_use]
72 pub fn content_hash(&self) -> Option<&str> {
73 self.content
74 .as_deref()
75 .and_then(|content| content.content_hash.as_deref())
76 }
77}
78
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
81pub struct ScanCacheStats {
82 pub reused_hashes: u64,
83 pub content_reads: u64,
84 #[cfg_attr(feature = "serde", serde(default))]
86 pub fingerprint_reads: u64,
87}
88
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
91pub enum SkipKind {
92 Binary,
93 FileSystemBoundary,
94 Extension,
95 Ignored,
96 IoError,
97 MaxDepth,
98 Oversized,
99 PathEscape,
100 StandardDirectory,
101 Hidden,
102 Override,
103 Symlink,
104 SymlinkLoop,
105 ScanLimit,
106 ConcurrentModification,
107}
108
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct SkippedEntry {
112 pub relative: String,
113 pub kind: SkipKind,
114 pub detail: Option<String>,
115}
116
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct ScanWarning {
120 pub relative: Option<String>,
121 pub message: String,
122}
123
124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
125#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
126pub enum IgnoreSourceKind {
127 GitGlobal,
128 GitExclude,
129 GitIgnore,
130 DotIgnore,
131 Custom,
132 Explicit,
133 Override,
134}
135
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct IgnoreSourceEvidence {
139 pub kind: IgnoreSourceKind,
140 pub location: String,
141 pub content_hash: String,
142}
143
144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum ScanTermination {
147 MaxEntries,
148 MaxTotalBytes,
149 Timeout,
150 Cancelled,
151}
152
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154#[derive(Debug, Clone, PartialEq, Eq)]
155pub struct ScanReport {
156 #[cfg_attr(feature = "serde", serde(with = "crate::path_serde"))]
157 pub root: PathBuf,
158 pub files: Vec<ScannedFile>,
159 pub skipped: Vec<SkippedEntry>,
160 pub warnings: Vec<ScanWarning>,
161 #[cfg_attr(feature = "serde", serde(default))]
163 pub ignore_sources: Vec<IgnoreSourceEvidence>,
164 pub revision: String,
165 #[cfg_attr(feature = "serde", serde(default = "default_complete"))]
167 pub complete: bool,
168 #[cfg_attr(feature = "serde", serde(default))]
170 pub termination: Option<ScanTermination>,
171 #[cfg_attr(feature = "serde", serde(default = "default_portable"))]
173 pub portable: bool,
174 #[cfg_attr(feature = "serde", serde(default))]
176 pub cache: ScanCacheStats,
177 #[cfg_attr(feature = "serde", serde(skip, default = "default_record_skipped"))]
178 record_skipped: bool,
179}
180
181#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187#[derive(Debug, Clone, PartialEq, Eq)]
188pub struct CompactScanReport {
189 #[cfg_attr(feature = "serde", serde(with = "crate::path_serde"))]
190 pub root: PathBuf,
191 pub files: Vec<CompactScannedFile>,
192 pub skipped: Vec<SkippedEntry>,
193 pub warnings: Vec<ScanWarning>,
194 #[cfg_attr(feature = "serde", serde(default))]
195 pub ignore_sources: Vec<IgnoreSourceEvidence>,
196 pub revision: String,
197 #[cfg_attr(feature = "serde", serde(default = "default_complete"))]
198 pub complete: bool,
199 #[cfg_attr(feature = "serde", serde(default))]
200 pub termination: Option<ScanTermination>,
201 #[cfg_attr(feature = "serde", serde(default = "default_portable"))]
202 pub portable: bool,
203 #[cfg_attr(feature = "serde", serde(default))]
204 pub cache: ScanCacheStats,
205}
206
207#[cfg(feature = "serde")]
208const fn default_complete() -> bool {
209 true
210}
211
212#[cfg(feature = "serde")]
213const fn default_record_skipped() -> bool {
214 true
215}
216
217#[cfg(feature = "serde")]
218const fn default_portable() -> bool {
219 true
220}