use thiserror::Error;
#[derive(Debug, Error)]
pub enum PatchExecutorError {
#[error("文件操作失败: {0}")]
IoError(#[from] std::io::Error),
#[error("路径错误: {path}")]
PathError { path: String },
#[error("权限错误: {path}")]
PermissionError { path: String },
#[error("原子操作失败: {reason}")]
AtomicOperationFailed { reason: String },
#[error("回滚失败: {reason}")]
RollbackFailed { reason: String },
#[error("补丁下载失败: {url}")]
DownloadFailed { url: String },
#[error("补丁验证失败: {reason}")]
VerificationFailed { reason: String },
#[error("补丁解压失败: {reason}")]
ExtractionFailed { reason: String },
#[error("哈希校验失败: 期望 {expected}, 实际 {actual}")]
HashMismatch { expected: String, actual: String },
#[error("数字签名验证失败: {reason}")]
SignatureVerificationFailed { reason: String },
#[error("不支持的操作: {operation}")]
UnsupportedOperation { operation: String },
#[error("备份模式未启用,无法执行回滚操作")]
BackupNotEnabled,
#[error("补丁源目录未设置")]
PatchSourceNotSet,
#[error("临时文件操作错误: {0}")]
TempFileError(#[from] tempfile::PersistError),
#[error("HTTP 请求错误: {0}")]
HttpError(#[from] reqwest::Error),
#[error("JSON 解析错误: {0}")]
JsonError(#[from] serde_json::Error),
#[error("ZIP 操作错误: {0}")]
ZipError(#[from] zip::result::ZipError),
#[error("文件系统扩展操作错误: {0}")]
FsExtraError(#[from] fs_extra::error::Error),
#[error("补丁执行错误: {message}")]
Custom { message: String },
}
impl PatchExecutorError {
pub fn custom<S: Into<String>>(message: S) -> Self {
Self::Custom {
message: message.into(),
}
}
pub fn path_error<S: Into<String>>(path: S) -> Self {
Self::PathError { path: path.into() }
}
pub fn permission_error<S: Into<String>>(path: S) -> Self {
Self::PermissionError { path: path.into() }
}
pub fn atomic_operation_failed<S: Into<String>>(reason: S) -> Self {
Self::AtomicOperationFailed {
reason: reason.into(),
}
}
pub fn rollback_failed<S: Into<String>>(reason: S) -> Self {
Self::RollbackFailed {
reason: reason.into(),
}
}
pub fn download_failed<S: Into<String>>(url: S) -> Self {
Self::DownloadFailed { url: url.into() }
}
pub fn verification_failed<S: Into<String>>(reason: S) -> Self {
Self::VerificationFailed {
reason: reason.into(),
}
}
pub fn extraction_failed<S: Into<String>>(reason: S) -> Self {
Self::ExtractionFailed {
reason: reason.into(),
}
}
pub fn hash_mismatch<S: Into<String>>(expected: S, actual: S) -> Self {
Self::HashMismatch {
expected: expected.into(),
actual: actual.into(),
}
}
pub fn signature_verification_failed<S: Into<String>>(reason: S) -> Self {
Self::SignatureVerificationFailed {
reason: reason.into(),
}
}
pub fn unsupported_operation<S: Into<String>>(operation: S) -> Self {
Self::UnsupportedOperation {
operation: operation.into(),
}
}
pub fn is_recoverable(&self) -> bool {
match self {
Self::IoError(_) => true,
Self::HttpError(_) => true,
Self::DownloadFailed { .. } => true,
Self::TempFileError(_) => true,
Self::VerificationFailed { .. } => false,
Self::HashMismatch { .. } => false,
Self::SignatureVerificationFailed { .. } => false,
Self::PermissionError { .. } => false,
Self::UnsupportedOperation { .. } => false,
Self::BackupNotEnabled => false,
Self::PatchSourceNotSet => false,
_ => true,
}
}
pub fn requires_rollback(&self) -> bool {
match self {
Self::VerificationFailed { .. } => false,
Self::HashMismatch { .. } => false,
Self::SignatureVerificationFailed { .. } => false,
Self::DownloadFailed { .. } => false,
Self::BackupNotEnabled => false,
Self::PatchSourceNotSet => false,
_ => true,
}
}
}
pub type Result<T> = std::result::Result<T, PatchExecutorError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let error = PatchExecutorError::custom("test error");
assert!(matches!(error, PatchExecutorError::Custom { .. }));
assert_eq!(error.to_string(), "补丁执行错误: test error");
}
#[test]
fn test_error_recoverability() {
let recoverable = PatchExecutorError::download_failed("http://example.com");
assert!(recoverable.is_recoverable());
let non_recoverable = PatchExecutorError::hash_mismatch("abc", "def");
assert!(!non_recoverable.is_recoverable());
}
#[test]
fn test_rollback_requirement() {
let requires_rollback = PatchExecutorError::atomic_operation_failed("test");
assert!(requires_rollback.requires_rollback());
let no_rollback = PatchExecutorError::verification_failed("test");
assert!(!no_rollback.requires_rollback());
}
}