1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum UpdateKitError {
6 #[error("Detection failed: {0}")]
7 DetectionFailed(String),
8
9 #[error("Network error: {0}")]
10 NetworkError(#[from] reqwest::Error),
11
12 #[error("Insecure URL rejected: {0}")]
13 InsecureUrl(String),
14
15 #[error("Download failed: {0}")]
16 DownloadFailed(String),
17
18 #[error("Checksum mismatch: expected {expected}, got {actual}")]
19 ChecksumMismatch { expected: String, actual: String },
20
21 #[error("Checksum missing: {0}")]
22 ChecksumMissing(String),
23
24 #[error("Checksum fetch failed: {0}")]
25 ChecksumFetchFailed(String),
26
27 #[error("Checksum parse failed: {0}")]
28 ChecksumParseFailed(String),
29
30 #[error("Extract failed: {0}")]
31 ExtractFailed(String),
32
33 #[error("Permission denied: {0}")]
34 PermissionDenied(String),
35
36 #[error("Command failed: {0}")]
37 CommandFailed(String),
38
39 #[error("Command timeout after {0}ms")]
40 CommandTimeout(u64),
41
42 #[error("Command aborted")]
43 CommandAborted,
44
45 #[error("Command spawn failed: {0}")]
46 CommandSpawnFailed(String),
47
48 #[error("Apply failed: {0}")]
49 ApplyFailed(String),
50
51 #[error("Cache error: {0}")]
52 CacheError(String),
53
54 #[error("Version parse error: {0}")]
55 VersionParse(String),
56
57 #[error("Unsupported platform: {0}")]
58 UnsupportedPlatform(String),
59
60 #[error("Unsupported operation: {0}")]
61 UnsupportedOperation(String),
62
63 #[error("IO error: {0}")]
64 Io(#[from] std::io::Error),
65
66 #[error("JSON error: {0}")]
67 Json(#[from] serde_json::Error),
68}
69
70impl UpdateKitError {
71 pub fn code(&self) -> &'static str {
73 match self {
74 UpdateKitError::DetectionFailed(_) => "DETECTION_FAILED",
75 UpdateKitError::NetworkError(_) => "NETWORK_ERROR",
76 UpdateKitError::InsecureUrl(_) => "INSECURE_URL",
77 UpdateKitError::DownloadFailed(_) => "DOWNLOAD_FAILED",
78 UpdateKitError::ChecksumMismatch { .. } => "CHECKSUM_MISMATCH",
79 UpdateKitError::ChecksumMissing(_) => "CHECKSUM_MISSING",
80 UpdateKitError::ChecksumFetchFailed(_) => "CHECKSUM_FETCH_FAILED",
81 UpdateKitError::ChecksumParseFailed(_) => "CHECKSUM_PARSE_FAILED",
82 UpdateKitError::ExtractFailed(_) => "EXTRACT_FAILED",
83 UpdateKitError::PermissionDenied(_) => "PERMISSION_DENIED",
84 UpdateKitError::CommandFailed(_) => "COMMAND_FAILED",
85 UpdateKitError::CommandTimeout(_) => "COMMAND_TIMEOUT",
86 UpdateKitError::CommandAborted => "COMMAND_ABORTED",
87 UpdateKitError::CommandSpawnFailed(_) => "COMMAND_SPAWN_FAILED",
88 UpdateKitError::ApplyFailed(_) => "APPLY_FAILED",
89 UpdateKitError::CacheError(_) => "CACHE_ERROR",
90 UpdateKitError::VersionParse(_) => "VERSION_PARSE",
91 UpdateKitError::UnsupportedPlatform(_) => "UNSUPPORTED_PLATFORM",
92 UpdateKitError::UnsupportedOperation(_) => "UNSUPPORTED_OPERATION",
93 UpdateKitError::Io(_) => "IO_ERROR",
94 UpdateKitError::Json(_) => "JSON_ERROR",
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_display_detection_failed() {
105 let err = UpdateKitError::DetectionFailed("no receipt".into());
106 assert_eq!(err.to_string(), "Detection failed: no receipt");
107 }
108
109 #[test]
110 fn test_display_checksum_mismatch() {
111 let err = UpdateKitError::ChecksumMismatch {
112 expected: "abc123".into(),
113 actual: "def456".into(),
114 };
115 assert_eq!(
116 err.to_string(),
117 "Checksum mismatch: expected abc123, got def456"
118 );
119 }
120
121 #[test]
122 fn test_display_command_timeout() {
123 let err = UpdateKitError::CommandTimeout(5000);
124 assert_eq!(err.to_string(), "Command timeout after 5000ms");
125 }
126
127 #[test]
128 fn test_display_command_aborted() {
129 let err = UpdateKitError::CommandAborted;
130 assert_eq!(err.to_string(), "Command aborted");
131 }
132
133 #[test]
134 fn test_code_values() {
135 assert_eq!(
136 UpdateKitError::DetectionFailed("x".into()).code(),
137 "DETECTION_FAILED"
138 );
139 assert_eq!(UpdateKitError::InsecureUrl("x".into()).code(), "INSECURE_URL");
140 assert_eq!(
141 UpdateKitError::ChecksumMismatch {
142 expected: "a".into(),
143 actual: "b".into()
144 }
145 .code(),
146 "CHECKSUM_MISMATCH"
147 );
148 assert_eq!(UpdateKitError::CommandTimeout(100).code(), "COMMAND_TIMEOUT");
149 assert_eq!(UpdateKitError::CommandAborted.code(), "COMMAND_ABORTED");
150 assert_eq!(
151 UpdateKitError::VersionParse("bad".into()).code(),
152 "VERSION_PARSE"
153 );
154 }
155
156 #[test]
157 fn test_from_io_error() {
158 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
159 let err: UpdateKitError = io_err.into();
160 assert_eq!(err.code(), "IO_ERROR");
161 assert!(err.to_string().contains("file not found"));
162 }
163
164 #[test]
165 fn test_from_json_error() {
166 let json_err = serde_json::from_str::<serde_json::Value>("invalid").unwrap_err();
167 let err: UpdateKitError = json_err.into();
168 assert_eq!(err.code(), "JSON_ERROR");
169 }
170}