client_core/patch_executor/
error.rs1use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum PatchExecutorError {
9 #[error("文件操作失败: {0}")]
11 IoError(#[from] std::io::Error),
12
13 #[error("路径错误: {path}")]
15 PathError { path: String },
16
17 #[error("权限错误: {path}")]
19 PermissionError { path: String },
20
21 #[error("原子操作失败: {reason}")]
23 AtomicOperationFailed { reason: String },
24
25 #[error("回滚失败: {reason}")]
27 RollbackFailed { reason: String },
28
29 #[error("补丁下载失败: {url}")]
31 DownloadFailed { url: String },
32
33 #[error("补丁验证失败: {reason}")]
35 VerificationFailed { reason: String },
36
37 #[error("补丁解压失败: {reason}")]
39 ExtractionFailed { reason: String },
40
41 #[error("哈希校验失败: 期望 {expected}, 实际 {actual}")]
43 HashMismatch { expected: String, actual: String },
44
45 #[error("数字签名验证失败: {reason}")]
47 SignatureVerificationFailed { reason: String },
48
49 #[error("不支持的操作: {operation}")]
51 UnsupportedOperation { operation: String },
52
53 #[error("备份模式未启用,无法执行回滚操作")]
55 BackupNotEnabled,
56
57 #[error("补丁源目录未设置")]
59 PatchSourceNotSet,
60
61 #[error("临时文件操作错误: {0}")]
63 TempFileError(#[from] tempfile::PersistError),
64
65 #[error("HTTP 请求错误: {0}")]
67 HttpError(#[from] reqwest::Error),
68
69 #[error("JSON 解析错误: {0}")]
71 JsonError(#[from] serde_json::Error),
72
73 #[error("ZIP 操作错误: {0}")]
75 ZipError(#[from] zip::result::ZipError),
76
77 #[error("文件系统扩展操作错误: {0}")]
79 FsExtraError(#[from] fs_extra::error::Error),
80
81 #[error("补丁执行错误: {message}")]
83 Custom { message: String },
84}
85
86impl PatchExecutorError {
87 pub fn custom<S: Into<String>>(message: S) -> Self {
89 Self::Custom {
90 message: message.into(),
91 }
92 }
93
94 pub fn path_error<S: Into<String>>(path: S) -> Self {
96 Self::PathError { path: path.into() }
97 }
98
99 pub fn permission_error<S: Into<String>>(path: S) -> Self {
101 Self::PermissionError { path: path.into() }
102 }
103
104 pub fn atomic_operation_failed<S: Into<String>>(reason: S) -> Self {
106 Self::AtomicOperationFailed {
107 reason: reason.into(),
108 }
109 }
110
111 pub fn rollback_failed<S: Into<String>>(reason: S) -> Self {
113 Self::RollbackFailed {
114 reason: reason.into(),
115 }
116 }
117
118 pub fn download_failed<S: Into<String>>(url: S) -> Self {
120 Self::DownloadFailed { url: url.into() }
121 }
122
123 pub fn verification_failed<S: Into<String>>(reason: S) -> Self {
125 Self::VerificationFailed {
126 reason: reason.into(),
127 }
128 }
129
130 pub fn extraction_failed<S: Into<String>>(reason: S) -> Self {
132 Self::ExtractionFailed {
133 reason: reason.into(),
134 }
135 }
136
137 pub fn hash_mismatch<S: Into<String>>(expected: S, actual: S) -> Self {
139 Self::HashMismatch {
140 expected: expected.into(),
141 actual: actual.into(),
142 }
143 }
144
145 pub fn signature_verification_failed<S: Into<String>>(reason: S) -> Self {
147 Self::SignatureVerificationFailed {
148 reason: reason.into(),
149 }
150 }
151
152 pub fn unsupported_operation<S: Into<String>>(operation: S) -> Self {
154 Self::UnsupportedOperation {
155 operation: operation.into(),
156 }
157 }
158
159 pub fn is_recoverable(&self) -> bool {
161 match self {
162 Self::IoError(_) => true,
163 Self::HttpError(_) => true,
164 Self::DownloadFailed { .. } => true,
165 Self::TempFileError(_) => true,
166 Self::VerificationFailed { .. } => false,
167 Self::HashMismatch { .. } => false,
168 Self::SignatureVerificationFailed { .. } => false,
169 Self::PermissionError { .. } => false,
170 Self::UnsupportedOperation { .. } => false,
171 Self::BackupNotEnabled => false,
172 Self::PatchSourceNotSet => false,
173 _ => true,
174 }
175 }
176
177 pub fn requires_rollback(&self) -> bool {
179 match self {
180 Self::VerificationFailed { .. } => false,
181 Self::HashMismatch { .. } => false,
182 Self::SignatureVerificationFailed { .. } => false,
183 Self::DownloadFailed { .. } => false,
184 Self::BackupNotEnabled => false,
185 Self::PatchSourceNotSet => false,
186 _ => true,
187 }
188 }
189}
190
191pub type Result<T> = std::result::Result<T, PatchExecutorError>;
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_error_creation() {
200 let error = PatchExecutorError::custom("test error");
201 assert!(matches!(error, PatchExecutorError::Custom { .. }));
202 assert_eq!(error.to_string(), "补丁执行错误: test error");
203 }
204
205 #[test]
206 fn test_error_recoverability() {
207 let recoverable = PatchExecutorError::download_failed("http://example.com");
208 assert!(recoverable.is_recoverable());
209
210 let non_recoverable = PatchExecutorError::hash_mismatch("abc", "def");
211 assert!(!non_recoverable.is_recoverable());
212 }
213
214 #[test]
215 fn test_rollback_requirement() {
216 let requires_rollback = PatchExecutorError::atomic_operation_failed("test");
217 assert!(requires_rollback.requires_rollback());
218
219 let no_rollback = PatchExecutorError::verification_failed("test");
220 assert!(!no_rollback.requires_rollback());
221 }
222}