Skip to main content

update_kit/
errors.rs

1use thiserror::Error;
2
3/// All error types for update-kit operations.
4#[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    /// Returns a static error code string for each variant.
72    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}