fs_more/error/directory.rs
1use std::path::PathBuf;
2
3use thiserror::Error;
4
5use super::FileError;
6use crate::directory::DestinationDirectoryRule;
7
8
9/// Source directory path validation error.
10#[derive(Error, Debug)]
11pub enum SourceDirectoryPathValidationError {
12 /// The source directory (path to the directory you want to copy)
13 /// does not exist.
14 #[error(
15 "source directory path does not exist: {}",
16 .directory_path.display()
17 )]
18 NotFound {
19 /// Source directory path.
20 directory_path: PathBuf,
21 },
22
23 /// The source path (path to the directory you want to copy)
24 /// exists, but does not point to a directory.
25 #[error(
26 "source path exists, but is not a directory: {}",
27 .path.display()
28 )]
29 NotADirectory {
30 /// The base source path that was supposed to be a directory.
31 path: PathBuf,
32 },
33
34 /// The source directory could not be read, or its path could not be canonicalized.
35 ///
36 /// Among other things, this can happen due to missing read permissions.
37 ///
38 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
39 #[error("unable to access source directory: {}", .directory_path.display())]
40 UnableToAccess {
41 /// The exact path we are unable to access.
42 directory_path: PathBuf,
43
44 /// IO error describing why the source directory could not be accessed.
45 #[source]
46 error: std::io::Error,
47 },
48}
49
50
51
52/// Destination directory path validation error.
53#[derive(Error, Debug)]
54pub enum DestinationDirectoryPathValidationError {
55 /// The base source path (path to the directory you want to copy)
56 /// exists, but does not point to a directory.
57 #[error(
58 "destination path exists, but is not a directory: {}",
59 .directory_path.display()
60 )]
61 NotADirectory {
62 /// Destination directory path.
63 directory_path: PathBuf,
64 },
65
66 /// The destination directory could not be read, or its path could not be canonicalized.
67 ///
68 /// Among other things, this can happen due to missing read permissions.
69 ///
70 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
71 #[error("unable to access destination directory: {}", .directory_path.display())]
72 UnableToAccess {
73 /// The exact path we were unable to access.
74 directory_path: PathBuf,
75
76 /// IO error describing why the source directory could not be accessed.
77 #[source]
78 error: std::io::Error,
79 },
80
81 /// A destination directory or a file inside it already exists,
82 /// which is against the provided [`DestinationDirectoryRule`].
83 #[error(
84 "destination path already exists, which is against \
85 the configured destination directory rule ({:?}): {}",
86 .destination_directory_rule,
87 .path.display()
88 )]
89 AlreadyExists {
90 /// Path to the file or directory that should not exist based on the provided rules.
91 path: PathBuf,
92
93 /// Destination directory rule that made the existing destination
94 /// directory invalid (see [`DestinationDirectoryRule::DisallowExisting`]).
95 destination_directory_rule: DestinationDirectoryRule,
96 },
97
98 /// A destination directory or a file inside it exists and is not empty,
99 /// which is against the provided [`DestinationDirectoryRule`].
100 #[error(
101 "destination directory exists and is not empty, which is against \
102 the configured destination directory rule ({:?}): {}",
103 .destination_directory_rule,
104 .directory_path.display(),
105 )]
106 NotEmpty {
107 /// Path to the destination directory that should be empty based on the provided rules.
108 directory_path: PathBuf,
109
110 /// Destination directory rule that made the existing destination
111 /// directory invalid (see [`DestinationDirectoryRule::AllowEmpty`]).
112 destination_directory_rule: DestinationDirectoryRule,
113 },
114
115 /// The destination directory path equals or points inside the source directory,
116 /// which is very problematic for copies or moves.
117 #[error(
118 "destination directory path equals or points inside the source directory, \
119 which is invalid: {} (but source path is {})",
120 .destination_directory_path.display(),
121 .source_directory_path.display()
122 )]
123 DescendantOfSourceDirectory {
124 /// Invalid destination directory path.
125 destination_directory_path: PathBuf,
126
127 /// Corresponding source directory path.
128 source_directory_path: PathBuf,
129 },
130}
131
132
133
134/// Directory copy or move planning error.
135#[derive(Error, Debug)]
136pub enum DirectoryExecutionPlanError {
137 /// A source or destination directory, one of its sub-directories or a file
138 /// in it (or its metadata) cannot be read.
139 ///
140 /// For example, this can happen due to missing read permissions.
141 ///
142 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
143 #[error("unable to access path: {}", .path.display())]
144 UnableToAccess {
145 /// The path we were unable to access.
146 path: PathBuf,
147
148 /// IO error describing why the path could not be accessed.
149 #[source]
150 error: std::io::Error,
151 },
152
153 /// An item inside the source directory "escaped" outside of
154 /// the base source directory.
155 ///
156 /// # Implementation detail
157 /// This is an extremely unlikely error, because its requirement
158 /// is that [`std::fs::read_dir`]'s iterator returns a directory entry
159 /// outside the provided directory path.
160 ///
161 /// Even though this seems extremely unlikely, a `panic!` would be
162 /// an extreme measure due to the many types of filesystems that exist.
163 /// Instead, treat this as a truly fatal error.
164 #[error(
165 "a directory entry inside the source directory escaped out of it: {}",
166 .path.display()
167 )]
168 EntryEscapesSourceDirectory {
169 /// The path that "escaped" the source directory.
170 path: PathBuf,
171 },
172
173 /// A destination directory or a file inside it already exists,
174 /// which is against the configured [`DestinationDirectoryRule`].
175 ///
176 /// This can also happen when we intended to copy a file to the destination,
177 /// but a directory with the same name appeared mid-copy
178 /// (an unavoidable [time-of-check time-of-use](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) bug).
179 ///
180 /// The `path` field contains the path that already existed, causing this error.
181 #[error("destination directory or file already exists: {}", .path.display())]
182 DestinationItemAlreadyExists {
183 /// Path of the target directory or file that already exists.
184 path: PathBuf,
185 },
186
187 /// A broken symbolic link has been encountered inside the source directory.
188 ///
189 /// This error can occur only when `broken_symlink_behaviour` is set to
190 /// [`BrokenSymlinkBehaviour::Abort`].
191 ///
192 ///
193 /// [`BrokenSymlinkBehaviour::Abort`]: crate::directory::BrokenSymlinkBehaviour::Abort
194 #[error(
195 "symbolic link inside source directory is broken, \
196 and the behaviour is set to abort"
197 )]
198 SymbolicLinkIsBroken {
199 /// Path of the broken symbolic link.
200 path: PathBuf,
201 },
202}
203
204
205
206/// An item inside the source directory "escaped" outside of
207/// the base source directory.
208///
209/// # Implementation detail
210/// This is an extremely unlikely error, because its requirement
211/// is that [`fs::read_dir`]'s iterator returns a directory entry
212/// outside the provided directory path.
213///
214/// Even though this seems extremely unlikely, a `panic!` would be
215/// an extreme measure due to the many types of filesystems that exist.
216/// Instead, treat this as a truly fatal error.
217#[derive(Error, Debug)]
218#[error(
219 "a directory entry inside the source directory escaped out of it: {}",
220 .path.display()
221)]
222pub(crate) struct SourceSubPathNotUnderBaseSourceDirectory {
223 /// The path that "escaped" the source directory.
224 pub(crate) path: PathBuf,
225}
226
227
228
229/// Directory copy preparation error.
230#[derive(Error, Debug)]
231pub enum CopyDirectoryPreparationError {
232 /// A source directory validation error.
233 #[error(transparent)]
234 SourceDirectoryValidationError(#[from] SourceDirectoryPathValidationError),
235
236 /// A destination directory validation error.
237 #[error(transparent)]
238 DestinationDirectoryValidationError(#[from] DestinationDirectoryPathValidationError),
239
240 /// A directory copy planning error.
241 #[error(transparent)]
242 CopyPlanningError(#[from] DirectoryExecutionPlanError),
243}
244
245
246
247/// Directory copy execution error.
248#[derive(Error, Debug)]
249pub enum CopyDirectoryExecutionError {
250 /// Failed to create a directory inside the destination folder.
251 ///
252 /// For example, this can happen due to missing write permissions.
253 ///
254 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
255 #[error("unable to create directory: {}", .directory_path.display())]
256 UnableToCreateDirectory {
257 /// Directory we were unable to create.
258 directory_path: PathBuf,
259
260 /// IO error describing why the directory could not be created.
261 #[source]
262 error: std::io::Error,
263 },
264
265 /// A file or directory inside the destination directory could not be accessed.
266 #[error("unable to access destination path: {}", .path.display())]
267 UnableToAccessDestination {
268 /// The path we were unable to access.
269 path: PathBuf,
270
271 /// IO error describing why the directory could not be created.
272 #[source]
273 error: std::io::Error,
274 },
275
276 /// An error occurred while trying to copy a file to the destination.
277 #[error(
278 "an error occurred while copying a file to the destination: {}",
279 .file_path.display(),
280 )]
281 FileCopyError {
282 /// The file path that could not be copied.
283 file_path: PathBuf,
284
285 /// The underlying file copying error.
286 #[source]
287 error: FileError,
288 },
289
290 /// An error occurred while trying to create a symlink at the destination.
291 #[error(
292 "failed while creating a symlink at {}",
293 .symlink_path.display()
294 )]
295 SymlinkCreationError {
296 /// The path to the symbolic link that could not be created.
297 symlink_path: PathBuf,
298
299 /// The underlying symlink creation error.
300 #[source]
301 error: std::io::Error,
302 },
303
304 /// A destination directory, a file, or a sub-directory inside it
305 /// has changed since the preparation phase of the directory copy.
306 ///
307 /// We can't guarantee that all destination directory changes
308 /// will trigger this, but some more obvious problematic ones, like
309 /// a file appearing in one of the destinations we wanted to copy to, will.
310 ///
311 /// This is essentially an unavoidable
312 /// [time-of-check time-of-use](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)
313 /// bug, but we try to catch it if possible.
314 ///
315 /// The `path` field contains the path that already existed, causing this error.
316 #[error("destination directory or file has been created externally mid-execution: {}", .path.display())]
317 DestinationEntryUnexpected {
318 /// The path of the target directory or file that already exists.
319 path: PathBuf,
320 },
321}
322
323
324
325/// Directory copying error (see [`copy_directory`] / [`copy_directory_with_progress`]).
326///
327///
328/// [`copy_directory`]: crate::directory::copy_directory
329/// [`copy_directory_with_progress`]: crate::directory::copy_directory_with_progress
330#[derive(Error, Debug)]
331pub enum CopyDirectoryError {
332 /// Directory copy preparation error.
333 #[error(transparent)]
334 PreparationError(#[from] CopyDirectoryPreparationError),
335
336 /// Directory copy execution error.
337 #[error(transparent)]
338 ExecutionError(#[from] CopyDirectoryExecutionError),
339}
340
341
342
343/// Directory move preparation error.
344#[derive(Error, Debug)]
345pub enum MoveDirectoryPreparationError {
346 /// Source directory validation error.
347 #[error(transparent)]
348 SourceDirectoryValidationError(#[from] SourceDirectoryPathValidationError),
349
350 /// Destination directory validation error.
351 #[error(transparent)]
352 DestinationDirectoryValidationError(#[from] DestinationDirectoryPathValidationError),
353
354 /// Source directory entry scanning error.
355 #[error(transparent)]
356 DirectoryScanError(#[from] DirectoryScanError),
357
358 /// Directory copy planning error. These errors can happen
359 /// when a move-by-rename fails and a copy-and-delete is attempted instead.
360 #[error(transparent)]
361 CopyPlanningError(#[from] DirectoryExecutionPlanError),
362}
363
364
365
366/// Directory move execution error.
367#[derive(Error, Debug)]
368pub enum MoveDirectoryExecutionError {
369 /// A file or directory inside the source directory could not be accessed.
370 #[error("unable to access source path: {}", .path.display())]
371 UnableToAccessSource {
372 /// The path we were unable to access.
373 path: PathBuf,
374
375 /// IO error describing why the directory could not be created.
376 #[source]
377 error: std::io::Error,
378 },
379
380 /// An item inside the source directory "escaped" outside of
381 /// the base source directory.
382 ///
383 /// # Implementation detail
384 /// This is an extremely unlikely error, because its requirement
385 /// is that [`std::fs::read_dir`]'s iterator returns a directory entry
386 /// outside the provided directory path.
387 ///
388 /// Even though this seems extremely unlikely, a `panic!` would be
389 /// an extreme measure due to the many types of filesystems that exist.
390 /// Instead, treat this as a truly fatal error.
391 #[error(
392 "a directory entry inside the source directory escaped out of it: {}",
393 .path.display()
394 )]
395 EntryEscapesSourceDirectory {
396 /// The path that "escaped" the source directory.
397 path: PathBuf,
398 },
399
400 /// A destination directory, a file, or a sub-directory inside it
401 /// has changed since the preparation phase of the directory move call.
402 ///
403 /// We can't guarantee that all destination directory changes
404 /// will trigger this, but some more obvious problematic ones, like
405 /// a file appearing in one of the destinations we wanted to copy to, will.
406 ///
407 /// This is essentially an unavoidable
408 /// [time-of-check time-of-use](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)
409 /// bug, but we try to catch it if possible.
410 ///
411 /// The `path` field contains the path that already existed, causing this error.
412 #[error("destination directory or file has been created externally mid-execution: {}", .path.display())]
413 DestinationEntryUnexpected {
414 /// The path of the target directory or file that already exists.
415 path: PathBuf,
416 },
417
418 /// Directory copy execution error.
419 ///
420 /// These errors can happen when a move-by-rename fails
421 /// and a copy-and-delete is performed instead.
422 #[error(transparent)]
423 CopyDirectoryError(#[from] CopyDirectoryExecutionError),
424
425 /// Occurs when renaming is the only enabled directory move strategy,
426 /// but it fails.
427 ///
428 /// This commonly indicates that the source and destination directory are
429 /// on different mount points, which would require copy-and-delete, and sometimes
430 /// even following (instead of preserving) symbolic links.
431 #[error(
432 "only rename strategy is enabled (with no copy-and-delete \
433 fallback strategy), but we were unable to rename the directory"
434 )]
435 RenameFailedAndNoFallbackStrategy,
436
437 /// An uncategorized unrecoverable IO error.
438 /// See `error` field for more information.
439 #[error("uncategorized std::io::Error")]
440 OtherIoError {
441 /// IO error describing the cause of the outer error.
442 #[source]
443 error: std::io::Error,
444 },
445}
446
447
448
449/// Directory moving error (see [`move_directory`] / [`move_directory_with_progress`]).
450///
451///
452/// [`move_directory`]: crate::directory::move_directory
453/// [`move_directory_with_progress`]: crate::directory::move_directory_with_progress
454#[derive(Error, Debug)]
455pub enum MoveDirectoryError {
456 /// Directory move preparation error.
457 #[error(transparent)]
458 PreparationError(#[from] MoveDirectoryPreparationError),
459
460 /// Directory move execution error.
461 #[error(transparent)]
462 ExecutionError(#[from] MoveDirectoryExecutionError),
463}
464
465
466
467/// An error that can occur when querying the size of a directory
468/// (see [`directory_size_in_bytes`]).
469///
470///
471/// [`directory_size_in_bytes`]: crate::directory::directory_size_in_bytes
472#[derive(Error, Debug)]
473pub enum DirectorySizeScanError {
474 /// An error occurred while scanning the directory.
475 #[error("failed while scanning directory: {}", .directory_path.display())]
476 ScanError {
477 /// The scanning error.
478 #[source]
479 error: DirectoryScanError,
480
481 /// Base directory path for the scan.
482 directory_path: PathBuf,
483 },
484}
485
486
487
488/// An error that can occur when checking whether a directory is empty
489/// (see [`is_directory_empty`]).
490///
491///
492/// [`is_directory_empty`]: crate::directory::is_directory_empty
493#[derive(Error, Debug)]
494pub enum DirectoryEmptinessScanError {
495 /// The provided directory path to scan doesn't exist.
496 #[error("path doesn't exist: {}", .path.display())]
497 NotFound {
498 /// The directory path that couldn't be scanned.
499 path: PathBuf,
500 },
501
502 /// The provided directory path exists, but is not a directory.
503 #[error(
504 "path exists, but is not a directory nor a symlink to one: {}",
505 .path.display()
506 )]
507 NotADirectory {
508 /// The directory path that couldn't be scanned.
509 path: PathBuf,
510 },
511
512 /// The provided directory path is a directory,
513 /// but could not be read due to an IO error.
514 ///
515 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
516 #[error("unable to read directory: {}", .directory_path.display())]
517 UnableToReadDirectory {
518 /// The directory path that could not be read.
519 directory_path: PathBuf,
520
521 /// IO error describing why the given root directory could not be read.
522 #[source]
523 error: std::io::Error,
524 },
525
526 /// A directory contains an entry (i.e. directory or file)
527 /// that could not be read due to an IO error.
528 ///
529 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
530 #[error("unable to read directory entry for {}", .directory_path.display())]
531 UnableToReadDirectoryEntry {
532 /// The directory path whose entries could not be read.
533 directory_path: PathBuf,
534
535 /// IO error describing why the given file or directory could not be read.
536 #[source]
537 error: std::io::Error,
538 },
539}
540
541
542
543
544/// An error that can occur when scanning a directory.
545#[derive(Error, Debug)]
546pub enum DirectoryScanError {
547 /// The provided directory path to scan doesn't exist.
548 #[error("path doesn't exist: {}", .path.display())]
549 NotFound {
550 /// The directory path that couldn't be scanned.
551 path: PathBuf,
552 },
553
554 /// The provided directory path exists, but is not a directory.
555 #[error(
556 "path exists, but is not a directory nor a symlink to one: {}",
557 .path.display()
558 )]
559 NotADirectory {
560 /// The directory path that couldn't be scanned.
561 path: PathBuf,
562 },
563
564 /// The provided directory path is a directory,
565 /// but could not be read due to an IO error.
566 ///
567 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
568 #[error("unable to read directory: {}", .directory_path.display())]
569 UnableToReadDirectory {
570 /// The drectory path that could not be read.
571 directory_path: PathBuf,
572
573 /// IO error describing why the given root directory could not be read.
574 #[source]
575 error: std::io::Error,
576 },
577
578 /// A directory contains an entry (i.e. directory or file)
579 /// that could not be read due to an IO error.
580 ///
581 /// The inner [`std::io::Error`] will likely describe a more precise cause of this error.
582 #[error("unable to read directory entry for {}", .directory_path.display())]
583 UnableToReadDirectoryEntry {
584 /// The directory path whose entries could not be read.
585 directory_path: PathBuf,
586
587 /// IO error describing why the given file or directory could not be read.
588 #[source]
589 error: std::io::Error,
590 },
591
592 /// A symlink inside the scan tree is cyclical.
593 #[error("encountered a directory symlink cycle at {}", .directory_path.display())]
594 SymlinkCycleEncountered {
595 /// The directory path at which the cycle loops around (i.e. where the cycle was detected).
596 directory_path: PathBuf,
597 },
598}