use std::path::PathBuf;
use thiserror::Error;
pub type UpgradeResult<T> = Result<T, UpgradeError>;
#[derive(Debug, Error, Clone)]
pub enum UpgradeError {
#[error("Registry error for package '{package}': {reason}")]
RegistryError {
package: String,
reason: String,
},
#[error("Package '{package}' not found in registry '{registry}'")]
PackageNotFound {
package: String,
registry: String,
},
#[error("Authentication failed for registry '{registry}': {reason}")]
AuthenticationFailed {
registry: String,
reason: String,
},
#[error("Registry request timed out after {timeout_secs} seconds for package '{package}'")]
RegistryTimeout {
package: String,
timeout_secs: u64,
},
#[error("Invalid registry response for package '{package}': {reason}")]
InvalidResponse {
package: String,
reason: String,
},
#[error("Failed to create backup at '{path}': {reason}")]
BackupFailed {
path: PathBuf,
reason: String,
},
#[error("No backup found at '{path}' for rollback")]
NoBackup {
path: PathBuf,
},
#[error("Rollback failed: {reason}")]
RollbackFailed {
reason: String,
},
#[error("Failed to apply upgrades to '{path}': {reason}")]
ApplyFailed {
path: PathBuf,
reason: String,
},
#[error("No upgrades available for any dependencies")]
NoUpgradesAvailable,
#[error("Invalid package name '{name}': {reason}")]
InvalidPackageName {
name: String,
reason: String,
},
#[error("Invalid version specification '{spec}' for package '{package}': {reason}")]
InvalidVersionSpec {
package: String,
spec: String,
reason: String,
},
#[error("Invalid version '{version}': {message}")]
InvalidVersion {
version: String,
message: String,
},
#[error("Failed to compare versions for package '{package}': {reason}")]
VersionComparisonFailed {
package: String,
reason: String,
},
#[error("Failed to parse .npmrc at '{path}': {reason}")]
NpmrcParseError {
path: PathBuf,
reason: String,
},
#[error("Filesystem error at '{path}': {reason}")]
FileSystemError {
path: PathBuf,
reason: String,
},
#[error("Failed to parse package.json at '{path}': {reason}")]
PackageJsonError {
path: PathBuf,
reason: String,
},
#[error("Package '{package}' is deprecated: {message}")]
DeprecatedPackage {
package: String,
message: String,
alternative: Option<String>,
},
#[error("Failed to create changeset for upgrades: {reason}")]
ChangesetCreationFailed {
reason: String,
},
#[error("Invalid upgrade configuration: {reason}")]
InvalidConfig {
reason: String,
},
#[error("Invalid workspace at '{path}': {reason}")]
InvalidWorkspace {
path: PathBuf,
reason: String,
},
#[error("No packages found in workspace at '{workspace_root}'")]
NoPackagesFound {
workspace_root: PathBuf,
},
#[error("Concurrent modification detected in '{path}'")]
ConcurrentModification {
path: PathBuf,
},
#[error("Network error: {reason}")]
NetworkError {
reason: String,
},
#[error("Rate limit exceeded for registry '{registry}': {reason}")]
RateLimitExceeded {
registry: String,
reason: String,
},
#[error("Maximum backup limit ({max_backups}) exceeded at '{path}'")]
MaxBackupsExceeded {
path: PathBuf,
max_backups: usize,
},
#[error("Backup corrupted at '{path}': {reason}")]
BackupCorrupted {
path: PathBuf,
reason: String,
},
}
impl AsRef<str> for UpgradeError {
fn as_ref(&self) -> &str {
match self {
Self::RegistryError { .. } => "registry error",
Self::PackageNotFound { .. } => "package not found",
Self::AuthenticationFailed { .. } => "authentication failed",
Self::RegistryTimeout { .. } => "registry timeout",
Self::InvalidResponse { .. } => "invalid response",
Self::BackupFailed { .. } => "backup failed",
Self::NoBackup { .. } => "no backup",
Self::RollbackFailed { .. } => "rollback failed",
Self::ApplyFailed { .. } => "apply failed",
Self::NoUpgradesAvailable => "no upgrades available",
Self::InvalidPackageName { .. } => "invalid package name",
Self::InvalidVersionSpec { .. } => "invalid version spec",
Self::InvalidVersion { .. } => "invalid version",
Self::VersionComparisonFailed { .. } => "version comparison failed",
Self::NpmrcParseError { .. } => "npmrc parse error",
Self::FileSystemError { .. } => "filesystem error",
Self::PackageJsonError { .. } => "package.json error",
Self::DeprecatedPackage { .. } => "deprecated package",
Self::ChangesetCreationFailed { .. } => "changeset creation failed",
Self::InvalidConfig { .. } => "invalid config",
Self::InvalidWorkspace { .. } => "invalid workspace",
Self::NoPackagesFound { .. } => "no packages found",
Self::ConcurrentModification { .. } => "concurrent modification",
Self::NetworkError { .. } => "network error",
Self::RateLimitExceeded { .. } => "rate limit exceeded",
Self::MaxBackupsExceeded { .. } => "max backups exceeded",
Self::BackupCorrupted { .. } => "backup corrupted",
}
}
}
impl UpgradeError {
#[must_use]
pub fn is_transient(&self) -> bool {
matches!(
self,
Self::RegistryTimeout { .. }
| Self::NetworkError { .. }
| Self::FileSystemError { .. }
| Self::ConcurrentModification { .. }
| Self::RegistryError { .. }
)
}
#[must_use]
pub fn is_registry_related(&self) -> bool {
matches!(
self,
Self::RegistryError { .. }
| Self::PackageNotFound { .. }
| Self::AuthenticationFailed { .. }
| Self::RegistryTimeout { .. }
| Self::InvalidResponse { .. }
| Self::NetworkError { .. }
| Self::RateLimitExceeded { .. }
)
}
#[must_use]
pub fn is_backup_related(&self) -> bool {
matches!(
self,
Self::BackupFailed { .. }
| Self::NoBackup { .. }
| Self::RollbackFailed { .. }
| Self::MaxBackupsExceeded { .. }
| Self::BackupCorrupted { .. }
)
}
#[must_use]
pub fn alternative(&self) -> Option<&String> {
match self {
Self::DeprecatedPackage { alternative, .. } => alternative.as_ref(),
_ => None,
}
}
}