1use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum ActrCliError {
10 #[error("Config error: {message}")]
11 Config { message: String },
12
13 #[error("Invalid project: {message}")]
14 InvalidProject { message: String },
15
16 #[error("Invalid argument: {message}")]
17 InvalidArgument { message: String },
18
19 #[error("Network error: {message}")]
20 Network { message: String },
21
22 #[error("Dependency error: {message}")]
23 Dependency { message: String },
24
25 #[error("Dependency conflict: {message}")]
26 DependencyConflict { message: String },
27
28 #[error("Service not found: {name}")]
29 ServiceNotFound { name: String },
30
31 #[error("Service discovery error: {message}")]
32 ServiceDiscovery { message: String },
33
34 #[error("Fingerprint validation error: {message}")]
35 FingerprintValidation { message: String },
36
37 #[error("Fingerprint mismatch: expected {expected}, got {actual}")]
38 FingerprintMismatch { expected: String, actual: String },
39
40 #[error("Compatibility conflict: {message}")]
41 CompatibilityConflict { message: String },
42
43 #[error("Code generation error: {message}")]
44 CodeGeneration { message: String },
45
46 #[error("Cache error: {message}")]
47 Cache { message: String },
48
49 #[error("User interface error: {message}")]
50 UserInterface { message: String },
51
52 #[error("Command execution error: {message}")]
53 Command { message: String },
54
55 #[error("Validation failed: {details}")]
56 ValidationFailed { details: String },
57
58 #[error("Install failed: {reason}")]
59 InstallFailed { reason: String },
60
61 #[error("Component not registered: {component}")]
62 ComponentNotRegistered { component: String },
63
64 #[error("Operation cancelled")]
65 OperationCancelled,
66
67 #[error("IO error")]
68 Io(#[from] std::io::Error),
69
70 #[error("Serialization error")]
71 Serialization(#[from] toml::de::Error),
72
73 #[error("HTTP error")]
74 Http(#[from] reqwest::Error),
75
76 #[error("Other error: {0}")]
77 Other(#[from] anyhow::Error),
78}
79
80#[derive(Debug, Error)]
82pub enum InstallError {
83 #[error("Dependency resolution failed: {dependency}")]
84 DependencyResolutionFailed { dependency: String },
85
86 #[error("Service unavailable: {service}")]
87 ServiceUnavailable { service: String },
88
89 #[error("Network connection failed")]
90 NetworkConnectionFailed,
91
92 #[error("Fingerprint mismatch: {service} - expected: {expected}, actual: {actual}")]
93 FingerprintMismatch {
94 service: String,
95 expected: String,
96 actual: String,
97 },
98
99 #[error("Version conflict: {details}")]
100 VersionConflict { details: String },
101
102 #[error("Cache operation failed: {operation}")]
103 CacheOperationFailed { operation: String },
104
105 #[error("Config update failed: {reason}")]
106 ConfigUpdateFailed { reason: String },
107
108 #[error("Pre-check failed: {failures:?}")]
109 PreCheckFailed { failures: Vec<String> },
110}
111
112#[derive(Debug, Error)]
114pub enum ValidationError {
115 #[error("Config file syntax error: {file}")]
116 ConfigSyntaxError { file: String },
117
118 #[error("Dependency not found: {dependency}")]
119 DependencyNotFound { dependency: String },
120
121 #[error("Network unreachable")]
122 NetworkUnreachable,
123
124 #[error("Fingerprint mismatch: {service}")]
125 FingerprintMismatch { service: String },
126
127 #[error("Circular dependency: {cycle}")]
128 CircularDependency { cycle: String },
129
130 #[error("Insufficient permissions: {resource}")]
131 InsufficientPermissions { resource: String },
132}
133
134impl ActrCliError {
136 pub fn user_message(&self) -> String {
138 match self {
139 ActrCliError::Config { message } => {
140 format!(
141 "ā ļø Config file error: {message}\nš” Hint: Check Actr.toml syntax and content"
142 )
143 }
144 ActrCliError::Network { message } => {
145 format!(
146 "š Network connection error: {message}\nš” Hint: Check network connection and service address"
147 )
148 }
149 ActrCliError::Dependency { message } => {
150 format!(
151 "š¦ Dependency error: {message}\nš” Hint: Run 'actr check' to check dependencies"
152 )
153 }
154 ActrCliError::ValidationFailed { details } => {
155 format!(
156 "ā Validation failed: {details}\nš” Hint: Fix the issues above and try again"
157 )
158 }
159 ActrCliError::InstallFailed { reason } => {
160 format!(
161 "š„ Install failed: {reason}\nš” Hint: Run 'actr check' to check environment"
162 )
163 }
164 _ => self.to_string(),
165 }
166 }
167
168 pub fn suggested_actions(&self) -> Vec<String> {
170 match self {
171 ActrCliError::Config { .. } => vec![
172 "Check Actr.toml file syntax".to_string(),
173 "Run 'actr config test' to validate config".to_string(),
174 "Refer to config examples in documentation".to_string(),
175 ],
176 ActrCliError::Network { .. } => vec![
177 "Check network connection".to_string(),
178 "Verify service address is correct".to_string(),
179 "Check firewall settings".to_string(),
180 "Run 'actr check --verbose' for details".to_string(),
181 ],
182 ActrCliError::Dependency { .. } => vec![
183 "Run 'actr check' to check dependency status".to_string(),
184 "Run 'actr install' to install missing dependencies".to_string(),
185 "Run 'actr discovery' to find available services".to_string(),
186 ],
187 ActrCliError::ValidationFailed { .. } => vec![
188 "Check and fix reported issues".to_string(),
189 "Run 'actr check --verbose' for detailed diagnostics".to_string(),
190 "Ensure all dependency services are available".to_string(),
191 ],
192 ActrCliError::InstallFailed { .. } => vec![
193 "Check disk space".to_string(),
194 "Check network connection".to_string(),
195 "Run 'actr check' to validate environment".to_string(),
196 "Try clearing cache and retry".to_string(),
197 ],
198 _ => vec!["View detailed error information".to_string()],
199 }
200 }
201
202 pub fn documentation_links(&self) -> Vec<(&str, &str)> {
204 match self {
205 ActrCliError::Config { .. } => vec![
206 ("Config Docs", "https://docs.actor-rtc.com/config"),
207 (
208 "Actr.toml Reference",
209 "https://docs.actor-rtc.com/actr-toml",
210 ),
211 ],
212 ActrCliError::Dependency { .. } => vec![
213 (
214 "Dependency Management",
215 "https://docs.actor-rtc.com/dependencies",
216 ),
217 (
218 "Troubleshooting",
219 "https://docs.actor-rtc.com/troubleshooting",
220 ),
221 ],
222 _ => vec![("User Guide", "https://docs.actor-rtc.com/guide")],
223 }
224 }
225}
226
227impl From<super::components::ValidationReport> for ActrCliError {
229 fn from(report: super::components::ValidationReport) -> Self {
230 let mut details = Vec::new();
231
232 if !report.config_validation.is_valid {
233 details.extend(
234 report
235 .config_validation
236 .errors
237 .iter()
238 .map(|e| format!("Config error: {e}")),
239 );
240 }
241
242 for dep in &report.dependency_validation {
243 if !dep.is_available {
244 details.push(format!(
245 "Dependency unavailable: {} - {}",
246 dep.dependency,
247 dep.error.as_deref().unwrap_or("unknown error")
248 ));
249 }
250 }
251
252 for net in &report.network_validation {
253 if !net.is_reachable {
254 details.push(format!(
255 "Network unreachable: {}",
256 net.error.as_deref().unwrap_or("connection failed")
257 ));
258 }
259 }
260
261 for fp in &report.fingerprint_validation {
262 if !fp.is_valid {
263 details.push(format!(
264 "Fingerprint validation failed: {} - {}",
265 fp.dependency,
266 fp.error.as_deref().unwrap_or("fingerprint mismatch")
267 ));
268 }
269 }
270
271 for conflict in &report.conflicts {
272 details.push(format!("Dependency conflict: {}", conflict.description));
273 }
274
275 ActrCliError::ValidationFailed {
276 details: details.join("; "),
277 }
278 }
279}
280
281pub struct ErrorReporter;
283
284impl ErrorReporter {
285 pub fn format_error(error: &ActrCliError) -> String {
287 let mut output = Vec::new();
288
289 output.push(error.user_message());
291 output.push(String::new());
292
293 let actions = error.suggested_actions();
295 if !actions.is_empty() {
296 output.push("š§ Suggested solutions:".to_string());
297 for (i, action) in actions.iter().enumerate() {
298 output.push(format!(" {}. {}", i + 1, action));
299 }
300 output.push(String::new());
301 }
302
303 let docs = error.documentation_links();
305 if !docs.is_empty() {
306 output.push("š Related documentation:".to_string());
307 for (title, url) in docs {
308 output.push(format!(" ⢠{title}: {url}"));
309 }
310 output.push(String::new());
311 }
312
313 output.join("\n")
314 }
315
316 pub fn format_validation_report(report: &super::components::ValidationReport) -> String {
318 let mut output = vec![
319 "š Dependency Validation Report".to_string(),
320 "=".repeat(50),
321 String::new(),
322 "š Config file validation:".to_string(),
323 ];
324
325 if report.config_validation.is_valid {
327 output.push(" ā
Passed".to_string());
328 } else {
329 output.push(" ā Failed".to_string());
330 for error in &report.config_validation.errors {
331 output.push(format!(" ⢠{error}"));
332 }
333 }
334 output.push(String::new());
335
336 output.push("š¦ Dependency availability:".to_string());
338 for dep in &report.dependency_validation {
339 if dep.is_available {
340 output.push(format!(" ā
{} - available", dep.dependency));
341 } else {
342 output.push(format!(
343 " ā {} - {}",
344 dep.dependency,
345 dep.error.as_deref().unwrap_or("unavailable")
346 ));
347 }
348 }
349 output.push(String::new());
350
351 output.push("š Network connectivity:".to_string());
353 for net in &report.network_validation {
354 if net.is_reachable {
355 let latency = net
356 .latency_ms
357 .map(|ms| format!(" ({ms}ms)"))
358 .unwrap_or_default();
359 output.push(format!(" ā
Connected{}", latency));
360 } else {
361 output.push(format!(
362 " ā Connection failed - {}",
363 net.error.as_deref().unwrap_or("unreachable")
364 ));
365 }
366 }
367 output.push(String::new());
368
369 if !report.fingerprint_validation.is_empty() {
371 output.push("š Fingerprint validation:".to_string());
372 for fp in &report.fingerprint_validation {
373 if fp.is_valid {
374 output.push(format!(" ā
{} - passed", fp.dependency));
375 } else {
376 output.push(format!(
377 " ā {} - {}",
378 fp.dependency,
379 fp.error.as_deref().unwrap_or("validation failed")
380 ));
381 }
382 }
383 output.push(String::new());
384 }
385
386 if !report.conflicts.is_empty() {
388 output.push("ā ļø Dependency conflicts:".to_string());
389 for conflict in &report.conflicts {
390 output.push(format!(
391 " ⢠{} vs {}: {}",
392 conflict.dependency_a, conflict.dependency_b, conflict.description
393 ));
394 }
395 output.push(String::new());
396 }
397
398 if report.is_success() {
400 output.push("⨠Overall: All validations passed".to_string());
401 } else {
402 output.push("ā Overall: Issues need to be resolved".to_string());
403 }
404
405 output.join("\n")
406 }
407}