impl AtomwriteError {
#[inline]
pub const fn exit_code(&self) -> u8 {
use crate::constants as k;
let code = match self {
Self::NotFound { .. } | Self::GlobNoMatches { .. } => k::EXIT_NOT_FOUND,
Self::InvalidInput { .. } => k::EXIT_INVALID_INPUT,
Self::MatchFailed { .. } | Self::MatchAmbiguous { .. } => k::EXIT_INVALID_INPUT,
Self::Cancelled { exit, .. } => return *exit,
Self::PermissionDenied { .. } => k::EXIT_PERMISSION_DENIED,
Self::DiskFull { .. } => k::EXIT_DISK_FULL,
Self::QuotaExceeded { .. } => k::EXIT_QUOTA_EXCEEDED,
Self::CrossDevice { .. } => k::EXIT_CROSS_DEVICE,
Self::Io { .. } => k::EXIT_IO_ERROR,
Self::ConfigInvalid { .. } => k::EXIT_CONFIG_INVALID,
Self::StateDrift { .. } => k::EXIT_STATE_DRIFT,
Self::ChecksumVerifyFailed { .. } => k::EXIT_CHECKSUM_VERIFY_FAILED,
Self::FileTooLarge { .. } => k::EXIT_INVALID_INPUT,
Self::WorkspaceJail { .. } => k::EXIT_WORKSPACE_JAIL,
Self::SymlinkBlocked { .. } => k::EXIT_SYMLINK_BLOCKED,
Self::FileImmutable { .. } => k::EXIT_IMMUTABLE_FILE,
Self::BinaryFile { .. } => k::EXIT_INVALID_INPUT,
Self::FifoDetected { .. } => k::EXIT_FIFO_DETECTED,
Self::DeviceFile { .. } => k::EXIT_DEVICE_FILE,
Self::NoMatches => k::EXIT_NO_MATCHES,
Self::AllSkippedBinary { .. } => k::EXIT_ALL_SKIPPED_BINARY,
Self::BrokenPipe => k::EXIT_BROKEN_PIPE,
Self::InternalError { .. } => k::EXIT_INTERNAL_ERROR,
Self::LockTimeout { .. } => k::EXIT_LOCK_TIMEOUT,
Self::SyntaxError { .. } => k::EXIT_SYNTAX_ERROR,
Self::ExdevFallbackDisabled { .. } => k::EXIT_EXDEV_FALLBACK_DISABLED,
Self::CopyBackBlake3Failed { .. } => k::EXIT_COPY_BACK_FAILED,
Self::OrphanJournal { .. } => k::EXIT_ORPHAN_JOURNAL,
Self::EditPairFailed { .. } => k::EXIT_INVALID_INPUT,
Self::MergeConflict { .. } => k::EXIT_MERGE_CONFLICT,
Self::EditLoopPartial { .. } => k::EXIT_EDIT_LOOP_PARTIAL,
Self::MissingFlag { .. } => k::EXIT_INVALID_INPUT,
Self::DoctorChecksFailed { .. } => k::EXIT_FAILURE,
Self::TransactionRollbackFailed { .. } => k::EXIT_TRANSACTION_ROLLBACK_FAILED,
Self::GrammarUnavailable { .. } => k::EXIT_GRAMMAR_UNAVAILABLE,
Self::UnsupportedCombination { .. } => k::EXIT_ARGUMENT_PARSE_ERROR,
};
code as u8
}
#[inline]
pub fn error_class(&self) -> ErrorClass {
match self {
Self::Io { source } => {
if source.raw_os_error() == Some(crate::constants::ERRNO_EISDIR)
|| source.kind() == std::io::ErrorKind::IsADirectory
|| source.kind() == std::io::ErrorKind::NotADirectory
|| source.kind() == std::io::ErrorKind::InvalidInput
{
ErrorClass::PreconditionFailed
} else {
ErrorClass::Transient
}
}
Self::DiskFull { .. } | Self::QuotaExceeded { .. } => ErrorClass::Transient,
Self::StateDrift { .. }
| Self::CrossDevice { .. }
| Self::LockTimeout { .. }
| Self::CopyBackBlake3Failed { .. } => ErrorClass::Conflict,
Self::ChecksumVerifyFailed { .. }
| Self::FileTooLarge { .. }
| Self::SyntaxError { .. }
| Self::ExdevFallbackDisabled { .. }
| Self::OrphanJournal { .. } => ErrorClass::PreconditionFailed,
Self::BinaryFile { .. }
| Self::FileImmutable { .. }
| Self::SymlinkBlocked { .. }
| Self::WorkspaceJail { .. }
| Self::FifoDetected { .. }
| Self::DeviceFile { .. }
| Self::DoctorChecksFailed { .. } => ErrorClass::PreconditionFailed,
Self::NoMatches | Self::AllSkippedBinary { .. } | Self::BrokenPipe => {
ErrorClass::Permanent
}
Self::MergeConflict { .. }
| Self::EditLoopPartial { .. }
| Self::MissingFlag { .. }
| Self::TransactionRollbackFailed { .. } => ErrorClass::Permanent,
Self::GrammarUnavailable { .. } => ErrorClass::PreconditionFailed,
Self::UnsupportedCombination { .. } => ErrorClass::Permanent,
_ => ErrorClass::Permanent,
}
}
#[inline]
pub fn is_retryable(&self) -> bool {
self.error_class().is_retryable()
}
#[inline]
pub fn is_permanent(&self) -> bool {
self.error_class().is_permanent()
}
#[inline]
pub const fn error_code(&self) -> &'static str {
match self {
Self::NotFound { .. } => "FILE_NOT_FOUND",
Self::GlobNoMatches { .. } => "GLOB_NO_MATCHES",
Self::InvalidInput { .. } => "INVALID_INPUT",
Self::MatchFailed { .. } => "MATCH_FAILED",
Self::MatchAmbiguous { .. } => "MATCH_AMBIGUOUS",
Self::Cancelled { .. } => "CANCELLED",
Self::PermissionDenied { .. } => "PERMISSION_DENIED",
Self::DiskFull { .. } => "DISK_FULL",
Self::QuotaExceeded { .. } => "QUOTA_EXCEEDED",
Self::CrossDevice { .. } => "CROSS_DEVICE",
Self::Io { .. } => "IO_ERROR",
Self::ConfigInvalid { .. } => "CONFIG_INVALID",
Self::StateDrift { .. } => "STATE_DRIFT",
Self::ChecksumVerifyFailed { .. } => "CHECKSUM_VERIFY_FAILED",
Self::FileTooLarge { .. } => "FILE_TOO_LARGE",
Self::WorkspaceJail { .. } => "WORKSPACE_JAIL",
Self::SymlinkBlocked { .. } => "SYMLINK_BLOCKED",
Self::FileImmutable { .. } => "IMMUTABLE_FILE",
Self::BinaryFile { .. } => "BINARY_FILE",
Self::FifoDetected { .. } => "FIFO_DETECTED",
Self::DeviceFile { .. } => "DEVICE_FILE",
Self::NoMatches => "NO_MATCHES",
Self::AllSkippedBinary { .. } => "ALL_SKIPPED_BINARY",
Self::BrokenPipe => "BROKEN_PIPE",
Self::InternalError { .. } => "INTERNAL_ERROR",
Self::LockTimeout { .. } => "LOCK_TIMEOUT",
Self::SyntaxError { .. } => "SYNTAX_ERROR_DETECTED",
Self::ExdevFallbackDisabled { .. } => "EXDEV_FALLBACK_DISABLED",
Self::CopyBackBlake3Failed { .. } => "COPY_BACK_BLAKE3_FAILED",
Self::OrphanJournal { .. } => "ORPHAN_JOURNAL",
Self::EditPairFailed { .. } => "MATCH_FAILED",
Self::MergeConflict { .. } => "MERGE_CONFLICT",
Self::EditLoopPartial { .. } => "EDIT_LOOP_PARTIAL",
Self::MissingFlag { .. } => "INVALID_INPUT",
Self::TransactionRollbackFailed { .. } => "TRANSACTION_ROLLBACK_FAILED",
Self::DoctorChecksFailed { .. } => "DOCTOR_CHECKS_FAILED",
Self::GrammarUnavailable { .. } => "GRAMMAR_UNAVAILABLE",
Self::UnsupportedCombination { .. } => "UNSUPPORTED_COMBINATION",
}
}
#[must_use]
pub fn invalid(what: &str, cause: impl std::fmt::Display) -> Self {
let text = cause.to_string();
let short = text
.lines()
.rev()
.map(str::trim)
.find(|line| line.chars().any(char::is_alphabetic))
.map_or(text.trim(), |line| line.trim_start_matches("error: "));
Self::InvalidInput {
reason: format!("{what}: {short}"),
}
}
#[must_use]
pub fn from_io_at(path: &std::path::Path, source: std::io::Error) -> Self {
match source.kind() {
std::io::ErrorKind::PermissionDenied => Self::PermissionDenied {
path: path.to_path_buf(),
},
std::io::ErrorKind::NotFound => Self::NotFound {
path: path.to_path_buf(),
},
kind => Self::Io {
source: std::io::Error::new(kind, format!("{}: {source}", path.display())),
},
}
}
#[must_use]
pub fn from_walk_error(err: &ignore::Error) -> Option<(PathBuf, Self)> {
fn io_cause(err: &ignore::Error) -> Option<std::io::Error> {
match err {
ignore::Error::Io(source) => {
Some(std::io::Error::new(source.kind(), source.to_string()))
}
ignore::Error::WithPath { err, .. }
| ignore::Error::WithDepth { err, .. }
| ignore::Error::WithLineNumber { err, .. } => io_cause(err),
_ => None,
}
}
match err {
ignore::Error::WithPath { path, err } => {
io_cause(err).map(|source| (path.clone(), Self::from_io_at(path, source)))
}
ignore::Error::WithDepth { err, .. } => Self::from_walk_error(err),
ignore::Error::Loop { child, .. } => Some((
child.clone(),
Self::from_io_at(child, std::io::Error::other(err.to_string())),
)),
_ => None,
}
}
#[inline]
pub fn path(&self) -> Option<&PathBuf> {
match self {
Self::NotFound { path }
| Self::GlobNoMatches { pattern: path }
| Self::PermissionDenied { path }
| Self::DiskFull { path }
| Self::QuotaExceeded { path }
| Self::CrossDevice { path }
| Self::StateDrift { path, .. }
| Self::ChecksumVerifyFailed { path, .. }
| Self::FileTooLarge { path, .. }
| Self::WorkspaceJail { path, .. }
| Self::SymlinkBlocked { path }
| Self::FileImmutable { path }
| Self::BinaryFile { path }
| Self::FifoDetected { path }
| Self::DeviceFile { path }
| Self::LockTimeout { path, .. }
| Self::SyntaxError { path, .. }
| Self::ExdevFallbackDisabled { path, .. }
| Self::CopyBackBlake3Failed { path }
| Self::OrphanJournal { journal: path, .. }
| Self::MergeConflict { path, .. } => Some(path),
Self::InvalidInput { .. }
| Self::EditPairFailed { .. }
| Self::Io { .. }
| Self::ConfigInvalid { .. }
| Self::NoMatches
| Self::AllSkippedBinary { .. }
| Self::BrokenPipe
| Self::InternalError { .. }
| Self::MatchFailed { .. }
| Self::MatchAmbiguous { .. }
| Self::Cancelled { .. }
| Self::EditLoopPartial { .. }
| Self::MissingFlag { .. }
| Self::DoctorChecksFailed { .. }
| Self::TransactionRollbackFailed { .. }
| Self::GrammarUnavailable { .. }
| Self::UnsupportedCombination { .. } => None,
}
}
}