weavatrix_scan/walk_types/
mod.rs1use crate::report::FileVersion;
2use std::ffi::OsStr;
3use std::fmt;
4use std::io;
5use std::path::{Path, PathBuf};
6
7mod entry;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub(crate) struct FileSystemId(pub(crate) u64);
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub(crate) struct DirectoryIdentity {
14 pub(crate) file_system: FileSystemId,
15 pub(crate) file: u64,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ErrorPolicy {
20 Continue,
21 Abort,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum RootSymlinkPolicy {
27 Follow,
29 Reject,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub struct WalkOptions {
35 pub min_depth: usize,
36 pub max_depth: Option<usize>,
37 pub max_open: usize,
38 pub same_file_system: bool,
39 pub follow_links: bool,
40 pub collect_metadata: bool,
41 pub error_policy: ErrorPolicy,
42 pub root_symlink_policy: RootSymlinkPolicy,
43}
44
45impl Default for WalkOptions {
46 fn default() -> Self {
47 Self {
48 min_depth: 0,
49 max_depth: None,
50 max_open: Self::DEFAULT_MAX_OPEN,
51 same_file_system: false,
52 follow_links: false,
53 collect_metadata: false,
54 error_policy: ErrorPolicy::Continue,
55 root_symlink_policy: RootSymlinkPolicy::Follow,
56 }
57 }
58}
59
60impl WalkOptions {
61 pub const DEFAULT_MAX_OPEN: usize = 64;
63
64 #[must_use]
65 pub const fn with_min_depth(mut self, min_depth: usize) -> Self {
66 self.min_depth = min_depth;
67 self
68 }
69
70 #[must_use]
71 pub const fn with_max_depth(mut self, max_depth: Option<usize>) -> Self {
72 self.max_depth = max_depth;
73 self
74 }
75
76 #[must_use]
77 pub const fn with_max_open(mut self, max_open: usize) -> Self {
78 self.max_open = if max_open == 0 { 1 } else { max_open };
79 self
80 }
81
82 #[must_use]
83 pub const fn with_same_file_system(mut self, enabled: bool) -> Self {
84 self.same_file_system = enabled;
85 self
86 }
87
88 #[must_use]
89 pub const fn with_follow_links(mut self, enabled: bool) -> Self {
90 self.follow_links = enabled;
91 self
92 }
93
94 #[must_use]
95 pub const fn with_metadata(mut self, enabled: bool) -> Self {
96 self.collect_metadata = enabled;
97 self
98 }
99
100 #[must_use]
101 pub const fn with_error_policy(mut self, policy: ErrorPolicy) -> Self {
102 self.error_policy = policy;
103 self
104 }
105
106 #[must_use]
107 pub const fn with_root_symlink_policy(mut self, policy: RootSymlinkPolicy) -> Self {
108 self.root_symlink_policy = policy;
109 self
110 }
111
112 pub(crate) fn normalized(mut self) -> Self {
113 self.max_open = self.max_open.max(1);
114 if let Some(max_depth) = self.max_depth
115 && self.min_depth > max_depth
116 {
117 self.min_depth = max_depth;
118 }
119 self
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum WalkOperation {
125 Canonicalize,
126 ReadDirectory,
127 ReadEntry,
128 ReadMetadata,
129 ScheduleWorker,
130}
131
132#[derive(Debug)]
133pub struct WalkError {
134 pub(crate) path: PathBuf,
135 pub(crate) depth: usize,
136 pub(crate) operation: WalkOperation,
137 pub(crate) source: io::Error,
138}
139
140impl WalkError {
141 pub(crate) fn new(
142 path: impl Into<PathBuf>,
143 depth: usize,
144 operation: WalkOperation,
145 source: io::Error,
146 ) -> Self {
147 Self {
148 path: path.into(),
149 depth,
150 operation,
151 source,
152 }
153 }
154
155 #[must_use]
156 pub fn path(&self) -> &Path {
157 &self.path
158 }
159
160 #[must_use]
161 pub const fn depth(&self) -> usize {
162 self.depth
163 }
164
165 #[must_use]
166 pub const fn operation(&self) -> WalkOperation {
167 self.operation
168 }
169
170 #[must_use]
171 pub const fn io_error(&self) -> &io::Error {
172 &self.source
173 }
174
175 pub(crate) fn into_parts(self) -> (PathBuf, io::Error) {
176 (self.path, self.source)
177 }
178
179 pub(crate) fn rebase_depth(&mut self, depth_offset: usize) {
180 self.depth += depth_offset;
181 }
182}
183
184impl fmt::Display for WalkError {
185 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
186 write!(
187 formatter,
188 "{} at depth {} for {}: {}",
189 operation_name(self.operation),
190 self.depth,
191 self.path.display(),
192 self.source
193 )
194 }
195}
196
197impl std::error::Error for WalkError {
198 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
199 Some(&self.source)
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq)]
204pub enum WalkSkipReason {
205 MaxDepth,
206 FileSystemBoundary,
207 PathEscape,
208 SymlinkLoop,
209}
210
211#[derive(Debug, Clone)]
212pub struct WalkEntry {
213 pub(crate) root_components: usize,
214 pub(crate) path: PathBuf,
215 pub(crate) depth: usize,
216 pub(crate) is_file: bool,
217 pub(crate) is_directory: bool,
218 pub(crate) is_symlink: bool,
219 pub(crate) bytes: Option<u64>,
220 pub(crate) version: Option<FileVersion>,
221 pub(crate) hidden: Option<bool>,
222 pub(crate) directory_identity: Option<DirectoryIdentity>,
223 pub(crate) skip_reason: Option<WalkSkipReason>,
224}
225
226const fn operation_name(operation: WalkOperation) -> &'static str {
227 match operation {
228 WalkOperation::Canonicalize => "canonicalize",
229 WalkOperation::ReadDirectory => "read directory",
230 WalkOperation::ReadEntry => "read entry",
231 WalkOperation::ReadMetadata => "read metadata",
232 WalkOperation::ScheduleWorker => "schedule worker",
233 }
234}