1use std::path::PathBuf;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug, thiserror::Error)]
10pub enum Error {
11 #[error("IO error: {0}")]
13 Io(#[from] std::io::Error),
14
15 #[error("HTTP request failed: {0}")]
17 Http(#[from] reqwest::Error),
18
19 #[error("Download failed from {url}: {reason}")]
21 DownloadFailed { url: String, reason: String },
22
23 #[error("Installation failed for {tool_name} v{version}: {message}")]
25 InstallationFailed {
26 tool_name: String,
27 version: String,
28 message: String,
29 },
30
31 #[error("Failed to extract archive {archive_path}: {reason}")]
33 ExtractionFailed {
34 archive_path: PathBuf,
35 reason: String,
36 },
37
38 #[error("Unsupported archive format: {format}")]
40 UnsupportedFormat { format: String },
41
42 #[error("Executable not found for {tool_name} in {search_path}")]
44 ExecutableNotFound {
45 tool_name: String,
46 search_path: PathBuf,
47 },
48
49 #[error("Checksum verification failed for {file_path}: expected {expected}, got {actual}")]
51 ChecksumMismatch {
52 file_path: PathBuf,
53 expected: String,
54 actual: String,
55 },
56
57 #[error("Invalid configuration: {message}")]
59 InvalidConfig { message: String },
60
61 #[error("Permission denied: {path}")]
63 PermissionDenied { path: PathBuf },
64
65 #[error("Tool {tool_name} v{version} is already installed")]
67 AlreadyInstalled { tool_name: String, version: String },
68
69 #[error("Insufficient disk space: required {required} bytes, available {available} bytes")]
71 InsufficientSpace { required: u64, available: u64 },
72
73 #[error("Network timeout while downloading from {url}")]
75 NetworkTimeout { url: String },
76
77 #[error("JSON parsing error: {0}")]
79 Json(#[from] serde_json::Error),
80
81 #[error("Directory traversal error: {0}")]
83 Walkdir(#[from] walkdir::Error),
84
85 #[error("Tool-specific error: {message}")]
87 ToolSpecific { message: String },
88}
89
90impl Error {
91 pub fn download_failed(url: impl Into<String>, reason: impl Into<String>) -> Self {
93 Self::DownloadFailed {
94 url: url.into(),
95 reason: reason.into(),
96 }
97 }
98
99 pub fn installation_failed(
101 tool_name: impl Into<String>,
102 version: impl Into<String>,
103 message: impl Into<String>,
104 ) -> Self {
105 Self::InstallationFailed {
106 tool_name: tool_name.into(),
107 version: version.into(),
108 message: message.into(),
109 }
110 }
111
112 pub fn extraction_failed(archive_path: impl Into<PathBuf>, reason: impl Into<String>) -> Self {
114 Self::ExtractionFailed {
115 archive_path: archive_path.into(),
116 reason: reason.into(),
117 }
118 }
119
120 pub fn unsupported_format(format: impl Into<String>) -> Self {
122 Self::UnsupportedFormat {
123 format: format.into(),
124 }
125 }
126
127 pub fn executable_not_found(
129 tool_name: impl Into<String>,
130 search_path: impl Into<PathBuf>,
131 ) -> Self {
132 Self::ExecutableNotFound {
133 tool_name: tool_name.into(),
134 search_path: search_path.into(),
135 }
136 }
137
138 pub fn is_recoverable(&self) -> bool {
140 matches!(
141 self,
142 Error::NetworkTimeout { .. }
143 | Error::Http(_)
144 | Error::DownloadFailed { .. }
145 | Error::InsufficientSpace { .. }
146 )
147 }
148
149 pub fn is_network_error(&self) -> bool {
151 matches!(
152 self,
153 Error::Http(_) | Error::DownloadFailed { .. } | Error::NetworkTimeout { .. }
154 )
155 }
156}