1use std::path::PathBuf;
2
3#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct ScannedFile {
6 #[cfg_attr(feature = "serde", serde(with = "crate::path_serde"))]
7 pub absolute: PathBuf,
8 pub relative: String,
9 pub bytes: u64,
10 pub content_hash: Option<String>,
11}
12
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
15pub enum SkipKind {
16 Binary,
17 FileSystemBoundary,
18 Extension,
19 Ignored,
20 IoError,
21 MaxDepth,
22 Oversized,
23 PathEscape,
24 StandardDirectory,
25 Hidden,
26 Override,
27 Symlink,
28 SymlinkLoop,
29 ScanLimit,
30}
31
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct SkippedEntry {
35 pub relative: String,
36 pub kind: SkipKind,
37 pub detail: Option<String>,
38}
39
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct ScanWarning {
43 pub relative: Option<String>,
44 pub message: String,
45}
46
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
49pub enum IgnoreSourceKind {
50 GitGlobal,
51 GitExclude,
52 GitIgnore,
53 DotIgnore,
54 Custom,
55 Explicit,
56 Override,
57}
58
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct IgnoreSourceEvidence {
62 pub kind: IgnoreSourceKind,
63 pub location: String,
64 pub content_hash: String,
65}
66
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum ScanTermination {
70 MaxEntries,
71 MaxTotalBytes,
72 Timeout,
73 Cancelled,
74}
75
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct ScanReport {
79 #[cfg_attr(feature = "serde", serde(with = "crate::path_serde"))]
80 pub root: PathBuf,
81 pub files: Vec<ScannedFile>,
82 pub skipped: Vec<SkippedEntry>,
83 pub warnings: Vec<ScanWarning>,
84 #[cfg_attr(feature = "serde", serde(default))]
86 pub ignore_sources: Vec<IgnoreSourceEvidence>,
87 pub revision: String,
88 #[cfg_attr(feature = "serde", serde(default = "default_complete"))]
90 pub complete: bool,
91 #[cfg_attr(feature = "serde", serde(default))]
93 pub termination: Option<ScanTermination>,
94 #[cfg_attr(feature = "serde", serde(default = "default_portable"))]
96 pub portable: bool,
97 #[cfg_attr(feature = "serde", serde(skip, default = "default_record_skipped"))]
98 record_skipped: bool,
99}
100
101#[cfg(feature = "serde")]
102const fn default_complete() -> bool {
103 true
104}
105
106#[cfg(feature = "serde")]
107const fn default_record_skipped() -> bool {
108 true
109}
110
111#[cfg(feature = "serde")]
112const fn default_portable() -> bool {
113 true
114}
115
116impl ScanReport {
117 #[must_use]
119 pub fn delta_from(&self, previous: &Self) -> crate::ScanDelta {
120 crate::ScanDelta::between(previous, self)
121 }
122
123 pub(crate) fn new(root: PathBuf, record_skipped: bool) -> Self {
124 Self {
125 root,
126 files: Vec::new(),
127 skipped: Vec::new(),
128 warnings: Vec::new(),
129 ignore_sources: Vec::new(),
130 revision: String::new(),
131 complete: true,
132 termination: None,
133 portable: true,
134 record_skipped,
135 }
136 }
137
138 pub(crate) fn skip(&mut self, relative: String, kind: SkipKind, detail: Option<String>) {
139 if self.record_skipped {
140 self.skipped.push(SkippedEntry {
141 relative,
142 kind,
143 detail,
144 });
145 }
146 }
147
148 pub(crate) fn warn(&mut self, relative: Option<String>, message: impl Into<String>) {
149 self.complete = false;
150 self.warnings.push(ScanWarning {
151 relative,
152 message: message.into(),
153 });
154 }
155
156 pub(crate) fn terminate(&mut self, reason: ScanTermination) {
157 self.complete = false;
158 self.termination.get_or_insert(reason);
159 }
160
161 pub(crate) fn finish_recording(&mut self) {
162 self.record_skipped = true;
163 }
164}