Skip to main content

actr_cli/core/
error.rs

1//! Unified Error Handling
2//!
3//! Defines unified error types and handling strategies for the CLI tool
4
5use thiserror::Error;
6
7/// CLI Unified Error Type
8#[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/// Install Error
81#[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/// Validation Error
113#[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
134/// User-friendly Error Display
135impl ActrCliError {
136    /// Get user-friendly error message
137    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    /// Get possible solutions
169    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    /// Get related documentation links
203    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
227/// Convert validation report to error
228impl 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
281/// Error Report Formatter
282pub struct ErrorReporter;
283
284impl ErrorReporter {
285    /// Format error report
286    pub fn format_error(error: &ActrCliError) -> String {
287        let mut output = Vec::new();
288
289        // Main error message
290        output.push(error.user_message());
291        output.push(String::new());
292
293        // Suggested solutions
294        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        // Documentation links
304        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    /// Format validation report
317    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        // Config validation
326        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        // Dependency validation
337        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        // Network validation
352        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        // Fingerprint validation
370        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        // Conflict report
387        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        // Summary
399        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}