Skip to main content

voirs_cli/error/
mod.rs

1//! Error handling system for the CLI.
2//!
3//! This module provides comprehensive error handling capabilities including
4//! basic CLI errors, advanced error handling with recovery, and formatting utilities.
5
6use std::fmt;
7
8use thiserror::Error;
9
10pub mod advanced_handler;
11
12/// CLI-specific error types
13#[derive(Error, Debug)]
14pub enum CliError {
15    /// Configuration error
16    #[error("Configuration error: {message}")]
17    Config { message: String },
18
19    /// File operation error
20    #[error("File operation failed: {operation} on {path}: {source}")]
21    File {
22        operation: String,
23        path: String,
24        #[source]
25        source: std::io::Error,
26    },
27
28    /// Audio format error
29    #[error("Unsupported audio format: {format}. Supported formats: {supported}")]
30    AudioFormat { format: String, supported: String },
31
32    /// Voice not found error
33    #[error("Voice '{voice_id}' not found. Available voices: {available}")]
34    VoiceNotFound { voice_id: String, available: String },
35
36    /// Invalid parameter error
37    #[error("Invalid parameter '{parameter}': {message}")]
38    InvalidParameter { parameter: String, message: String },
39
40    /// SDK error wrapper
41    #[error("VoiRS SDK error: {0}")]
42    Sdk(#[from] voirs_sdk::VoirsError),
43
44    /// Serialization error
45    #[error("Serialization error: {0}")]
46    Serialization(#[from] serde_json::Error),
47
48    /// TOML serialization error
49    #[error("TOML error: {0}")]
50    Toml(#[from] toml::ser::Error),
51
52    /// IO error
53    #[error("IO error: {0}")]
54    Io(#[from] std::io::Error),
55
56    /// Voice error for interactive mode
57    #[error("Voice error: {0}")]
58    VoiceError(String),
59
60    /// Audio error for interactive mode
61    #[error("Audio error: {0}")]
62    AudioError(String),
63
64    /// Synthesis error for interactive mode
65    #[error("Synthesis error: {0}")]
66    SynthesisError(String),
67
68    /// Validation error
69    #[error("Validation error: {0}")]
70    ValidationError(String),
71
72    /// IO error with message
73    #[error("IO error: {0}")]
74    IoError(String),
75
76    /// Serialization error with message
77    #[error("Serialization error: {0}")]
78    SerializationError(String),
79
80    /// Invalid argument error
81    #[error("Invalid argument: {0}")]
82    InvalidArgument(String),
83
84    /// Not implemented error
85    #[error("Not implemented: {0}")]
86    NotImplemented(String),
87
88    /// Interactive error
89    #[error("Interactive error: {0}")]
90    InteractiveError(String),
91
92    /// Packaging error
93    #[error("Packaging error: {0}")]
94    PackagingError(String),
95
96    /// Update error
97    #[error("Update error: {0}")]
98    UpdateError(String),
99
100    /// Emotion control error
101    #[error("Emotion control error: {0}")]
102    EmotionError(String),
103
104    /// Voice cloning error
105    #[error("Voice cloning error: {0}")]
106    CloningError(String),
107
108    /// Voice conversion error
109    #[error("Voice conversion error: {0}")]
110    ConversionError(String),
111
112    /// Singing synthesis error
113    #[error("Singing synthesis error: {0}")]
114    SingingError(String),
115
116    /// Spatial audio error
117    #[error("Spatial audio error: {0}")]
118    SpatialError(String),
119
120    /// Model loading error
121    #[error("Model loading error: {0}")]
122    ModelLoadingError(String),
123
124    /// Feature not available error
125    #[error("Feature not available: {0}")]
126    FeatureNotAvailable(String),
127
128    /// Dependency missing error
129    #[error("Missing dependency: {0}")]
130    MissingDependency(String),
131
132    /// Hardware requirement error
133    #[error("Hardware requirement not met: {0}")]
134    HardwareRequirement(String),
135
136    /// Network error
137    #[error("Network error: {0}")]
138    NetworkError(String),
139
140    /// Performance warning
141    #[error("Performance warning: {0}")]
142    PerformanceWarning(String),
143
144    /// Workflow execution error
145    #[error("Workflow error: {0}")]
146    Workflow(String),
147
148    /// Advanced error with rich context
149    #[error("Advanced error: {0}")]
150    Advanced(#[from] Box<advanced_handler::AdvancedError>),
151}
152
153impl CliError {
154    /// Create a configuration error
155    pub fn config<S: Into<String>>(message: S) -> Self {
156        Self::Config {
157            message: message.into(),
158        }
159    }
160
161    /// Create a file operation error
162    pub fn file_operation<S: Into<String>>(operation: S, path: S, source: std::io::Error) -> Self {
163        Self::File {
164            operation: operation.into(),
165            path: path.into(),
166            source,
167        }
168    }
169
170    /// Create an audio format error
171    pub fn audio_format<S: Into<String>>(format: S) -> Self {
172        Self::AudioFormat {
173            format: format.into(),
174            supported: "wav, flac, mp3, opus, ogg".to_string(),
175        }
176    }
177
178    /// Create a voice not found error
179    pub fn voice_not_found<S: Into<String>>(voice_id: S, available: Vec<String>) -> Self {
180        Self::VoiceNotFound {
181            voice_id: voice_id.into(),
182            available: available.join(", "),
183        }
184    }
185
186    /// Create an invalid parameter error
187    pub fn invalid_parameter<S: Into<String>>(parameter: S, message: S) -> Self {
188        Self::InvalidParameter {
189            parameter: parameter.into(),
190            message: message.into(),
191        }
192    }
193
194    /// Create an emotion control error
195    pub fn emotion_error<S: Into<String>>(message: S) -> Self {
196        Self::EmotionError(message.into())
197    }
198
199    /// Create a voice cloning error
200    pub fn cloning_error<S: Into<String>>(message: S) -> Self {
201        Self::CloningError(message.into())
202    }
203
204    /// Create a voice conversion error
205    pub fn conversion_error<S: Into<String>>(message: S) -> Self {
206        Self::ConversionError(message.into())
207    }
208
209    /// Create a singing synthesis error
210    pub fn singing_error<S: Into<String>>(message: S) -> Self {
211        Self::SingingError(message.into())
212    }
213
214    /// Create a spatial audio error
215    pub fn spatial_error<S: Into<String>>(message: S) -> Self {
216        Self::SpatialError(message.into())
217    }
218
219    /// Create a model loading error
220    pub fn model_loading_error<S: Into<String>>(message: S) -> Self {
221        Self::ModelLoadingError(message.into())
222    }
223
224    /// Create a feature not available error
225    pub fn feature_not_available<S: Into<String>>(message: S) -> Self {
226        Self::FeatureNotAvailable(message.into())
227    }
228
229    /// Create a missing dependency error
230    pub fn missing_dependency<S: Into<String>>(message: S) -> Self {
231        Self::MissingDependency(message.into())
232    }
233
234    /// Create a hardware requirement error
235    pub fn hardware_requirement<S: Into<String>>(message: S) -> Self {
236        Self::HardwareRequirement(message.into())
237    }
238
239    /// Create a network error
240    pub fn network_error<S: Into<String>>(message: S) -> Self {
241        Self::NetworkError(message.into())
242    }
243
244    /// Create a performance warning
245    pub fn performance_warning<S: Into<String>>(message: S) -> Self {
246        Self::PerformanceWarning(message.into())
247    }
248
249    /// Convert to an advanced error with additional context
250    pub fn to_advanced_error(&self) -> advanced_handler::AdvancedError {
251        use advanced_handler::*;
252        use std::collections::HashMap;
253        use std::time::{SystemTime, UNIX_EPOCH};
254
255        let (category, severity) = match self {
256            CliError::Config { .. } => (ErrorCategory::Configuration, ErrorSeverity::Error),
257            CliError::File { .. } => (ErrorCategory::FileSystem, ErrorSeverity::Error),
258            CliError::AudioFormat { .. } => (ErrorCategory::UserInput, ErrorSeverity::Error),
259            CliError::VoiceNotFound { .. } => (ErrorCategory::UserInput, ErrorSeverity::Error),
260            CliError::InvalidParameter { .. } => (ErrorCategory::UserInput, ErrorSeverity::Error),
261            CliError::NetworkError(_) => (ErrorCategory::Network, ErrorSeverity::Error),
262            CliError::ModelLoadingError(_) => (ErrorCategory::ModelLoading, ErrorSeverity::Error),
263            CliError::AudioError(_) => (ErrorCategory::AudioProcessing, ErrorSeverity::Error),
264            CliError::SynthesisError(_) => (ErrorCategory::Synthesis, ErrorSeverity::Error),
265            CliError::MissingDependency(_) => (ErrorCategory::Dependency, ErrorSeverity::Error),
266            CliError::HardwareRequirement(_) => (ErrorCategory::Hardware, ErrorSeverity::Error),
267            CliError::PerformanceWarning(_) => {
268                (ErrorCategory::ResourceExhaustion, ErrorSeverity::Warning)
269            }
270            _ => (ErrorCategory::Internal, ErrorSeverity::Error),
271        };
272
273        let context = ErrorContext {
274            operation: "cli_operation".to_string(),
275            user: std::env::var("USER").ok(),
276            session_id: None,
277            request_id: None,
278            component: "voirs-cli".to_string(),
279            function: None,
280            location: None,
281            parameters: HashMap::new(),
282            system_state: SystemState {
283                available_memory_bytes: 0, // Would be populated from system info
284                cpu_usage_percent: 0.0,
285                active_operations: 0,
286                queue_depth: 0,
287                last_success_time: None,
288                uptime_seconds: 0,
289            },
290            performance_metrics: None,
291        };
292
293        let recovery_suggestions = self.generate_recovery_suggestions();
294
295        AdvancedError {
296            category,
297            severity,
298            message: self.to_string(),
299            technical_details: format!("CLI Error: {:?}", self),
300            context,
301            recovery_suggestions,
302            related_errors: vec![],
303            timestamp: SystemTime::now()
304                .duration_since(UNIX_EPOCH)
305                .unwrap_or_default()
306                .as_secs(),
307            error_id: format!("cli_error_{}", fastrand::u64(..)),
308            recoverable: self.is_recoverable(),
309            retry_info: if self.is_recoverable() {
310                Some(RetryInfo {
311                    attempt: 0,
312                    max_attempts: 3,
313                    retry_delay: std::time::Duration::from_secs(1),
314                    backoff_strategy: BackoffStrategy::Exponential,
315                    last_retry: None,
316                    success_history: vec![],
317                })
318            } else {
319                None
320            },
321        }
322    }
323
324    /// Generate recovery suggestions for this error
325    fn generate_recovery_suggestions(&self) -> Vec<advanced_handler::RecoverySuggestion> {
326        use advanced_handler::*;
327        use std::collections::HashMap;
328
329        match self {
330            CliError::NetworkError(_) => vec![RecoverySuggestion {
331                category: RecoveryCategory::NetworkTroubleshooting,
332                priority: 8,
333                suggestion: "Check internet connection and retry".to_string(),
334                steps: vec![
335                    "Check internet connectivity".to_string(),
336                    "Verify DNS resolution".to_string(),
337                    "Retry the operation".to_string(),
338                ],
339                estimated_time: std::time::Duration::from_secs(30),
340                difficulty: DifficultyLevel::Easy,
341                success_probability: 0.8,
342                requires_user_action: true,
343                automated_actions: vec![AutomatedAction {
344                    action_type: ActionType::RetryOperation,
345                    description: "Retry with exponential backoff".to_string(),
346                    parameters: HashMap::new(),
347                    safe_to_automate: true,
348                    execution_time: std::time::Duration::from_secs(10),
349                    dependencies: vec![],
350                }],
351            }],
352            CliError::Config { .. } => vec![RecoverySuggestion {
353                category: RecoveryCategory::ConfigurationFix,
354                priority: 7,
355                suggestion: "Initialize default configuration".to_string(),
356                steps: vec![
357                    "Run 'voirs config --init'".to_string(),
358                    "Verify configuration with 'voirs config --show'".to_string(),
359                ],
360                estimated_time: std::time::Duration::from_secs(10),
361                difficulty: DifficultyLevel::Easy,
362                success_probability: 0.9,
363                requires_user_action: true,
364                automated_actions: vec![],
365            }],
366            CliError::ModelLoadingError(_) => vec![RecoverySuggestion {
367                category: RecoveryCategory::ResourceOptimization,
368                priority: 6,
369                suggestion: "Validate and re-download model".to_string(),
370                steps: vec![
371                    "Check available models with 'voirs list-models'".to_string(),
372                    "Download required model".to_string(),
373                    "Verify model integrity".to_string(),
374                ],
375                estimated_time: std::time::Duration::from_secs(300),
376                difficulty: DifficultyLevel::Medium,
377                success_probability: 0.7,
378                requires_user_action: true,
379                automated_actions: vec![AutomatedAction {
380                    action_type: ActionType::ValidateConfiguration,
381                    description: "Validate model files".to_string(),
382                    parameters: HashMap::new(),
383                    safe_to_automate: true,
384                    execution_time: std::time::Duration::from_secs(30),
385                    dependencies: vec![],
386                }],
387            }],
388            _ => vec![RecoverySuggestion {
389                category: RecoveryCategory::RetryOptimization,
390                priority: 5,
391                suggestion: "Retry the operation".to_string(),
392                steps: vec![
393                    "Wait a moment".to_string(),
394                    "Retry the same command".to_string(),
395                ],
396                estimated_time: std::time::Duration::from_secs(5),
397                difficulty: DifficultyLevel::Trivial,
398                success_probability: 0.5,
399                requires_user_action: true,
400                automated_actions: vec![],
401            }],
402        }
403    }
404
405    /// Check if this error type is recoverable
406    fn is_recoverable(&self) -> bool {
407        match self {
408            CliError::NetworkError(_) => true,
409            CliError::Config { .. } => true,
410            CliError::File { .. } => true,
411            CliError::ModelLoadingError(_) => true,
412            CliError::MissingDependency(_) => true,
413            CliError::InvalidParameter { .. } => false,
414            CliError::AudioFormat { .. } => false,
415            CliError::VoiceNotFound { .. } => false,
416            _ => true,
417        }
418    }
419
420    /// Get user-friendly error message with suggestions
421    pub fn user_message(&self) -> String {
422        match self {
423            CliError::Config { message } => {
424                format!("Configuration error: {}\n\nTry running 'voirs config --init' to create a default configuration.", message)
425            }
426            CliError::File {
427                operation,
428                path,
429                source,
430            } => {
431                format!("Failed to {} '{}': {}\n\nPlease check that the path exists and you have the necessary permissions.", operation, path, source)
432            }
433            CliError::AudioFormat { format, supported } => {
434                format!("Unsupported audio format: '{}'\n\nSupported formats: {}\n\nExample: voirs synthesize \"Hello\" --output audio.wav", format, supported)
435            }
436            CliError::VoiceNotFound {
437                voice_id,
438                available,
439            } => {
440                format!("Voice '{}' not found.\n\nAvailable voices: {}\n\nUse 'voirs voices list' to see all available voices.", voice_id, available)
441            }
442            CliError::InvalidParameter { parameter, message } => {
443                format!(
444                    "Invalid parameter '{}': {}\n\nUse 'voirs --help' for usage information.",
445                    parameter, message
446                )
447            }
448            CliError::Sdk(e) => {
449                format!("VoiRS synthesis error: {}\n\nThis might be a model loading or synthesis issue. Try checking your voice installation with 'voirs voices list'.", e)
450            }
451            CliError::Serialization(e) => {
452                format!("Data serialization error: {}\n\nThis might indicate a configuration file corruption.", e)
453            }
454            CliError::Toml(e) => {
455                format!(
456                    "TOML configuration error: {}\n\nPlease check your configuration file syntax.",
457                    e
458                )
459            }
460            CliError::Io(e) => {
461                format!("File system error: {}\n\nPlease check file permissions and available disk space.", e)
462            }
463            CliError::VoiceError(e) => {
464                format!(
465                    "Voice error: {}\n\nPlease check available voices with 'voirs voices list'.",
466                    e
467                )
468            }
469            CliError::AudioError(e) => {
470                format!(
471                    "Audio system error: {}\n\nPlease check your audio device configuration.",
472                    e
473                )
474            }
475            CliError::SynthesisError(e) => {
476                format!(
477                    "Synthesis error: {}\n\nThis might be a model or configuration issue.",
478                    e
479                )
480            }
481            CliError::ValidationError(e) => {
482                format!("Validation error: {}\n\nPlease check your input format.", e)
483            }
484            CliError::IoError(e) => {
485                format!(
486                    "I/O error: {}\n\nPlease check file paths and permissions.",
487                    e
488                )
489            }
490            CliError::SerializationError(e) => {
491                format!(
492                    "Serialization error: {}\n\nThis might indicate corrupted data.",
493                    e
494                )
495            }
496            CliError::InvalidArgument(e) => {
497                format!(
498                    "Invalid argument: {}\n\nPlease check command usage with --help.",
499                    e
500                )
501            }
502            CliError::NotImplemented(e) => {
503                format!(
504                    "Feature not implemented: {}\n\nThis feature is coming in a future update.",
505                    e
506                )
507            }
508            CliError::InteractiveError(e) => {
509                format!(
510                    "Interactive mode error: {}\n\nTry restarting the interactive session.",
511                    e
512                )
513            }
514            CliError::PackagingError(e) => {
515                format!(
516                    "Packaging error: {}\n\nPlease check your packaging configuration and dependencies.",
517                    e
518                )
519            }
520            CliError::UpdateError(e) => {
521                format!(
522                    "Update error: {}\n\nPlease check your network connection and try again.",
523                    e
524                )
525            }
526            CliError::EmotionError(e) => {
527                format!(
528                    "Emotion control error: {}\n\nSuggestions:\n- Check if emotion models are installed\n- Verify emotion intensity (0.0-1.0)\n- Use 'voirs capabilities check emotion' to verify feature availability\n- Use 'voirs emotion list' to see available emotions",
529                    e
530                )
531            }
532            CliError::CloningError(e) => {
533                format!(
534                    "Voice cloning error: {}\n\nSuggestions:\n- Ensure reference audio is high quality (16kHz+, clear speech)\n- Use at least 30 seconds of reference audio\n- Check if voice cloning models are installed\n- Use 'voirs capabilities check cloning' to verify feature availability\n- Use 'voirs clone validate' to check reference audio quality",
535                    e
536                )
537            }
538            CliError::ConversionError(e) => {
539                format!(
540                    "Voice conversion error: {}\n\nSuggestions:\n- Check if voice conversion models are installed\n- Verify input audio quality and format\n- Use 'voirs capabilities check conversion' to verify feature availability\n- Try reducing conversion intensity for better results",
541                    e
542                )
543            }
544            CliError::SingingError(e) => {
545                format!(
546                    "Singing synthesis error: {}\n\nSuggestions:\n- Check if singing models are installed\n- Verify musical score format (MusicXML, MIDI)\n- Ensure lyrics are properly formatted\n- Use 'voirs capabilities check singing' to verify feature availability\n- Use 'voirs sing validate' to check score format",
547                    e
548                )
549            }
550            CliError::SpatialError(e) => {
551                format!(
552                    "Spatial audio error: {}\n\nSuggestions:\n- Check if spatial audio models are installed\n- Verify HRTF dataset is available\n- Use 'voirs capabilities check spatial' to verify feature availability\n- Ensure position coordinates are valid (x,y,z format)",
553                    e
554                )
555            }
556            CliError::ModelLoadingError(e) => {
557                format!(
558                    "Model loading error: {}\n\nSuggestions:\n- Check if the required model files are present\n- Verify model file integrity\n- Use 'voirs list-models' to see available models\n- Try downloading the model again with 'voirs download-model'\n- Check available disk space and memory",
559                    e
560                )
561            }
562            CliError::FeatureNotAvailable(e) => {
563                format!(
564                    "Feature not available: {}\n\nSuggestions:\n- Check if the feature is enabled in your configuration\n- Verify feature dependencies are installed\n- Use 'voirs capabilities list' to see all available features\n- Check if your VoiRS version supports this feature",
565                    e
566                )
567            }
568            CliError::MissingDependency(e) => {
569                format!(
570                    "Missing dependency: {}\n\nSuggestions:\n- Install the required dependency\n- Use 'voirs capabilities requirements' to see all requirements\n- Check the installation documentation\n- Verify your system meets all prerequisites",
571                    e
572                )
573            }
574            CliError::HardwareRequirement(e) => {
575                format!(
576                    "Hardware requirement not met: {}\n\nSuggestions:\n- Check if your hardware meets the minimum requirements\n- Try using CPU-only mode if GPU is not available\n- Reduce quality settings for better performance\n- Use 'voirs capabilities list' to see system requirements",
577                    e
578                )
579            }
580            CliError::NetworkError(e) => {
581                format!(
582                    "Network error: {}\n\nSuggestions:\n- Check your internet connection\n- Verify proxy settings if applicable\n- Try again later if the server is temporarily unavailable\n- Check firewall settings",
583                    e
584                )
585            }
586            CliError::PerformanceWarning(e) => {
587                format!(
588                    "Performance warning: {}\n\nSuggestions:\n- Consider using GPU acceleration with --gpu flag\n- Reduce quality settings for faster processing\n- Close other resource-intensive applications\n- Use 'voirs capabilities list' to check system capabilities",
589                    e
590                )
591            }
592            CliError::Workflow(e) => {
593                format!(
594                    "Workflow execution error: {}\n\nSuggestions:\n- Check workflow definition syntax\n- Verify all step dependencies exist\n- Use 'voirs workflow validate' to check workflow\n- Check workflow state with 'voirs workflow status'",
595                    e
596                )
597            }
598            CliError::Advanced(advanced_error) => {
599                // For advanced errors, we'd typically use the advanced handler's user report
600                format!("Advanced error: {}", advanced_error.message)
601            }
602        }
603    }
604
605    /// Get exit code for this error
606    pub fn exit_code(&self) -> i32 {
607        match self {
608            CliError::Config { .. } => 1,
609            CliError::File { .. } => 2,
610            CliError::AudioFormat { .. } => 3,
611            CliError::VoiceNotFound { .. } => 4,
612            CliError::InvalidParameter { .. } => 5,
613            CliError::Sdk(_) => 10,
614            CliError::Serialization(_) => 11,
615            CliError::Toml(_) => 11,
616            CliError::Io(_) => 12,
617            CliError::VoiceError(_) => 13,
618            CliError::AudioError(_) => 14,
619            CliError::SynthesisError(_) => 15,
620            CliError::ValidationError(_) => 16,
621            CliError::IoError(_) => 17,
622            CliError::SerializationError(_) => 17,
623            CliError::InvalidArgument(_) => 18,
624            CliError::NotImplemented(_) => 19,
625            CliError::InteractiveError(_) => 20,
626            CliError::PackagingError(_) => 21,
627            CliError::UpdateError(_) => 22,
628            CliError::EmotionError(_) => 30,
629            CliError::CloningError(_) => 31,
630            CliError::ConversionError(_) => 32,
631            CliError::SingingError(_) => 33,
632            CliError::SpatialError(_) => 34,
633            CliError::ModelLoadingError(_) => 35,
634            CliError::FeatureNotAvailable(_) => 36,
635            CliError::MissingDependency(_) => 37,
636            CliError::HardwareRequirement(_) => 38,
637            CliError::NetworkError(_) => 39,
638            CliError::PerformanceWarning(_) => 40,
639            CliError::Workflow(_) => 41,
640            CliError::Advanced(_) => 50,
641        }
642    }
643}
644
645/// Result type for CLI operations
646pub type Result<T> = std::result::Result<T, CliError>;
647pub type VoirsCliError = CliError;
648pub type VoirsCLIError = CliError;
649
650/// Convert CliError to VoirsError for compatibility
651impl From<CliError> for voirs_sdk::VoirsError {
652    fn from(err: CliError) -> Self {
653        match err {
654            CliError::Sdk(voirs_err) => voirs_err,
655            other => voirs_sdk::VoirsError::InternalError {
656                component: "voirs-cli".to_string(),
657                message: other.to_string(),
658            },
659        }
660    }
661}
662
663/// Helper trait for adding context to errors
664pub trait ErrorContext<T> {
665    fn with_context<F, S>(self, f: F) -> Result<T>
666    where
667        F: FnOnce() -> S,
668        S: Into<String>;
669}
670
671impl<T> ErrorContext<T> for std::result::Result<T, std::io::Error> {
672    fn with_context<F, S>(self, f: F) -> Result<T>
673    where
674        F: FnOnce() -> S,
675        S: Into<String>,
676    {
677        self.map_err(|e| CliError::config(f().into()))
678    }
679}
680
681/// Error message formatting utilities
682pub mod formatting {
683    use super::CliError;
684    use crate::output::get_formatter;
685
686    /// Print error message with proper formatting
687    pub fn print_error(error: &CliError) {
688        let formatter = get_formatter();
689        formatter.error(&error.user_message());
690
691        // Print suggestions based on error type
692        match error {
693            CliError::VoiceNotFound { .. } => {
694                formatter.info("Suggested commands:");
695                formatter.list_item("voirs voices list", 1);
696                formatter.list_item("voirs voices download <voice-id>", 1);
697            }
698            CliError::AudioFormat { .. } => {
699                formatter.info("Example usage:");
700                formatter.list_item("voirs synthesize \"Hello\" -o output.wav", 1);
701                formatter.list_item("voirs synthesize \"Hello\" -o output.mp3", 1);
702            }
703            CliError::Config { .. } => {
704                formatter.info("Configuration commands:");
705                formatter.list_item("voirs config --init", 1);
706                formatter.list_item("voirs config --show", 1);
707            }
708            CliError::EmotionError(_) => {
709                formatter.info("Emotion control commands:");
710                formatter.list_item("voirs emotion list", 1);
711                formatter.list_item("voirs capabilities check emotion", 1);
712                formatter.list_item("voirs guide emotion", 1);
713            }
714            CliError::CloningError(_) => {
715                formatter.info("Voice cloning commands:");
716                formatter.list_item("voirs clone validate --reference-files samples/*.wav", 1);
717                formatter.list_item("voirs capabilities check cloning", 1);
718                formatter.list_item("voirs guide clone", 1);
719            }
720            CliError::ConversionError(_) => {
721                formatter.info("Voice conversion commands:");
722                formatter.list_item("voirs convert list-models", 1);
723                formatter.list_item("voirs capabilities check conversion", 1);
724                formatter.list_item("voirs guide convert", 1);
725            }
726            CliError::SingingError(_) => {
727                formatter.info("Singing synthesis commands:");
728                formatter.list_item("voirs sing validate --score score.xml", 1);
729                formatter.list_item("voirs capabilities check singing", 1);
730                formatter.list_item("voirs guide sing", 1);
731            }
732            CliError::SpatialError(_) => {
733                formatter.info("Spatial audio commands:");
734                formatter.list_item("voirs spatial validate --test-audio test.wav", 1);
735                formatter.list_item("voirs capabilities check spatial", 1);
736                formatter.list_item("voirs guide spatial", 1);
737            }
738            CliError::ModelLoadingError(_) => {
739                formatter.info("Model management commands:");
740                formatter.list_item("voirs list-models", 1);
741                formatter.list_item("voirs download-model <model-id>", 1);
742                formatter.list_item("voirs capabilities requirements", 1);
743            }
744            CliError::FeatureNotAvailable(_) => {
745                formatter.info("Feature diagnosis commands:");
746                formatter.list_item("voirs capabilities list", 1);
747                formatter.list_item("voirs capabilities requirements", 1);
748                formatter.list_item("voirs config --show", 1);
749            }
750            CliError::MissingDependency(_) => {
751                formatter.info("Dependency check commands:");
752                formatter.list_item("voirs capabilities requirements", 1);
753                formatter.list_item("voirs test", 1);
754            }
755            CliError::HardwareRequirement(_) => {
756                formatter.info("Hardware check commands:");
757                formatter.list_item("voirs capabilities list", 1);
758                formatter.list_item("voirs test --gpu", 1);
759            }
760            CliError::NetworkError(_) => {
761                formatter.info("Network troubleshooting:");
762                formatter.list_item("Check internet connectivity", 1);
763                formatter.list_item("Verify proxy settings", 1);
764                formatter.list_item("Try again later", 1);
765            }
766            CliError::PerformanceWarning(_) => {
767                formatter.info("Performance optimization:");
768                formatter.list_item("Use --gpu flag for acceleration", 1);
769                formatter.list_item("Reduce quality settings", 1);
770                formatter.list_item("Close other applications", 1);
771            }
772            CliError::Advanced(advanced_error) => {
773                // For advanced errors, we could display more detailed recovery suggestions
774                formatter.info("Advanced error detected - recovery suggestions available");
775                for suggestion in &advanced_error.recovery_suggestions {
776                    formatter.list_item(&suggestion.suggestion, 1);
777                }
778            }
779            _ => {}
780        }
781    }
782
783    /// Print warning message
784    pub fn print_warning(message: &str) {
785        get_formatter().warning(message);
786    }
787
788    /// Print info message
789    pub fn print_info(message: &str) {
790        get_formatter().info(message);
791    }
792}