use std::path::PathBuf;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("HTTP request failed: {0}")]
Http(#[from] reqwest::Error),
#[error("Download failed from {url}: {reason}")]
DownloadFailed { url: String, reason: String },
#[error("Installation failed for {tool_name} v{version}: {message}")]
InstallationFailed {
tool_name: String,
version: String,
message: String,
},
#[error("Failed to extract archive {archive_path}: {reason}")]
ExtractionFailed {
archive_path: PathBuf,
reason: String,
},
#[error("Unsupported archive format: {format}")]
UnsupportedFormat { format: String },
#[error("Executable not found for {tool_name} in {search_path}")]
ExecutableNotFound {
tool_name: String,
search_path: PathBuf,
},
#[error("Checksum verification failed for {file_path}: expected {expected}, got {actual}")]
ChecksumMismatch {
file_path: PathBuf,
expected: String,
actual: String,
},
#[error("Invalid configuration: {message}")]
InvalidConfig { message: String },
#[error("Permission denied: {path}")]
PermissionDenied { path: PathBuf },
#[error("Tool {tool_name} v{version} is already installed")]
AlreadyInstalled { tool_name: String, version: String },
#[error("Insufficient disk space: required {required} bytes, available {available} bytes")]
InsufficientSpace { required: u64, available: u64 },
#[error("Network timeout while downloading from {url}")]
NetworkTimeout { url: String },
#[error("JSON parsing error: {0}")]
Json(#[from] serde_json::Error),
#[error("Directory traversal error: {0}")]
Walkdir(#[from] walkdir::Error),
#[error("Tool-specific error: {message}")]
ToolSpecific { message: String },
}
impl Error {
pub fn download_failed(url: impl Into<String>, reason: impl Into<String>) -> Self {
Self::DownloadFailed {
url: url.into(),
reason: reason.into(),
}
}
pub fn installation_failed(
tool_name: impl Into<String>,
version: impl Into<String>,
message: impl Into<String>,
) -> Self {
Self::InstallationFailed {
tool_name: tool_name.into(),
version: version.into(),
message: message.into(),
}
}
pub fn extraction_failed(archive_path: impl Into<PathBuf>, reason: impl Into<String>) -> Self {
Self::ExtractionFailed {
archive_path: archive_path.into(),
reason: reason.into(),
}
}
pub fn unsupported_format(format: impl Into<String>) -> Self {
Self::UnsupportedFormat {
format: format.into(),
}
}
pub fn executable_not_found(
tool_name: impl Into<String>,
search_path: impl Into<PathBuf>,
) -> Self {
Self::ExecutableNotFound {
tool_name: tool_name.into(),
search_path: search_path.into(),
}
}
pub fn is_recoverable(&self) -> bool {
matches!(
self,
Error::NetworkTimeout { .. }
| Error::Http(_)
| Error::DownloadFailed { .. }
| Error::InsufficientSpace { .. }
)
}
pub fn is_network_error(&self) -> bool {
matches!(
self,
Error::Http(_) | Error::DownloadFailed { .. } | Error::NetworkTimeout { .. }
)
}
}