use std::io;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("source not found: {path}")]
SourceNotFound { path: PathBuf },
#[error("destination already exists: {path}")]
DestExists { path: PathBuf },
#[error("operation cancelled")]
Cancelled,
#[error("insufficient disk space: needed {needed} bytes, available {available} bytes")]
NoSpace { needed: u64, available: u64 },
#[error("permission denied: {path}")]
PermissionDenied { path: PathBuf },
#[error("io error on {path}: {source}")]
Io { path: PathBuf, source: io::Error },
#[cfg(feature = "compress")]
#[error("could not infer compression format from destination: {path}")]
UnknownCompressFormat { path: PathBuf },
#[cfg(any(feature = "analyze", feature = "remove"))]
#[error("invalid glob pattern {pattern:?}: {source}")]
InvalidGlobPattern {
pattern: String,
source: globset::Error,
},
#[cfg(feature = "compress")]
#[error("gzip compression requires a single file, got a directory: {path}")]
GzipRequiresFile { path: PathBuf },
#[cfg(feature = "operations")]
#[error("filename differs only by case from another entry, which the destination filesystem cannot represent: {path} collides with {other}")]
CaseCollision { path: PathBuf, other: PathBuf },
#[cfg(feature = "operations")]
#[error("file exceeds the destination filesystem's maximum file size: {path} ({size} bytes, max {max} bytes)")]
FileTooLargeForDest { path: PathBuf, size: u64, max: u64 },
#[cfg(feature = "operations")]
#[error("filename is reserved or invalid on the destination filesystem: {path}")]
ReservedName { path: PathBuf },
#[cfg(feature = "operations")]
#[error(
"destination filesystem ({filesystem}) has a known write-integrity issue on this platform"
)]
FilesystemIntegrityRisk { filesystem: String },
#[cfg(feature = "operations")]
#[error("two sources would both move to the same destination name: {path} and {other}")]
DuplicateSourceName { path: PathBuf, other: PathBuf },
#[cfg(feature = "operations")]
#[error("source path has no file name to move under: {path}")]
InvalidSourceName { path: PathBuf },
#[cfg(feature = "remove")]
#[error(
"remove() called with no filter criteria set, which would match every entry under the root"
)]
RemoveCriteriaRequired,
#[cfg(feature = "remove")]
#[error("could not move to trash: {path}: {source}")]
TrashFailed { path: PathBuf, source: trash::Error },
}
impl Error {
#[cfg(feature = "operations")]
pub(crate) fn is_fatal(&self) -> bool {
matches!(
self,
Error::Cancelled
| Error::NoSpace { .. }
| Error::FilesystemIntegrityRisk { .. }
| Error::DuplicateSourceName { .. }
| Error::InvalidSourceName { .. }
)
}
}
#[cfg(any(feature = "operations", feature = "watch", feature = "analyze"))]
pub(crate) fn classify_io_error(err: io::Error, path: PathBuf, needed: u64) -> Error {
match err.kind() {
io::ErrorKind::NotFound => Error::SourceNotFound { path },
io::ErrorKind::PermissionDenied => Error::PermissionDenied { path },
io::ErrorKind::StorageFull => Error::NoSpace {
needed,
available: 0,
},
_ => Error::Io { path, source: err },
}
}
pub type Result<T> = std::result::Result<T, Error>;