sakurs_cli/
error.rs

1//! Error handling for the CLI application
2
3use std::fmt;
4
5/// Custom error type for CLI-specific errors
6#[derive(Debug)]
7pub enum CliError {
8    /// File not found or inaccessible
9    FileNotFound(String),
10    /// Invalid file pattern
11    InvalidPattern(String),
12    /// Configuration error
13    ConfigError(String),
14    /// Processing error from core
15    ProcessingError(String),
16}
17
18impl fmt::Display for CliError {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            CliError::FileNotFound(path) => write!(f, "File not found: {path}"),
22            CliError::InvalidPattern(pattern) => write!(f, "Invalid file pattern: {pattern}"),
23            CliError::ConfigError(msg) => write!(f, "Configuration error: {msg}"),
24            CliError::ProcessingError(msg) => write!(f, "Processing error: {msg}"),
25        }
26    }
27}
28
29impl std::error::Error for CliError {}
30
31/// Result type alias for CLI operations
32pub type CliResult<T> = Result<T, anyhow::Error>;
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37
38    #[test]
39    fn test_file_not_found_error_display() {
40        let error = CliError::FileNotFound("test.txt".to_string());
41        assert_eq!(error.to_string(), "File not found: test.txt");
42    }
43
44    #[test]
45    fn test_invalid_pattern_error_display() {
46        let error = CliError::InvalidPattern("[invalid".to_string());
47        assert_eq!(error.to_string(), "Invalid file pattern: [invalid");
48    }
49
50    #[test]
51    fn test_config_error_display() {
52        let error = CliError::ConfigError("invalid format".to_string());
53        assert_eq!(error.to_string(), "Configuration error: invalid format");
54    }
55
56    #[test]
57    fn test_processing_error_display() {
58        let error = CliError::ProcessingError("parse failed".to_string());
59        assert_eq!(error.to_string(), "Processing error: parse failed");
60    }
61
62    #[test]
63    fn test_error_trait_implementation() {
64        let error = CliError::FileNotFound("test.txt".to_string());
65        // Test that it implements std::error::Error
66        let _: &dyn std::error::Error = &error;
67
68        // Test Debug formatting
69        let debug_str = format!("{:?}", error);
70        assert!(debug_str.contains("FileNotFound"));
71        assert!(debug_str.contains("test.txt"));
72    }
73
74    #[test]
75    fn test_cli_result_type_alias() {
76        // Test successful result
77        let success: CliResult<String> = Ok("test".to_string());
78        assert!(success.is_ok());
79        assert_eq!(success.as_ref().unwrap(), "test");
80
81        // Test error result
82        let failure: CliResult<String> = Err(anyhow::anyhow!("test error"));
83        assert!(failure.is_err());
84        assert!(failure
85            .as_ref()
86            .unwrap_err()
87            .to_string()
88            .contains("test error"));
89    }
90
91    #[test]
92    fn test_all_error_variants_creation() {
93        // Test that all enum variants can be created
94        let file_error = CliError::FileNotFound("/path/to/file.txt".to_string());
95        let pattern_error = CliError::InvalidPattern("*.{".to_string());
96        let config_error = CliError::ConfigError("missing field 'language'".to_string());
97        let processing_error = CliError::ProcessingError("segmentation fault".to_string());
98
99        // Verify they all implement Display properly
100        assert!(file_error.to_string().starts_with("File not found:"));
101        assert!(pattern_error
102            .to_string()
103            .starts_with("Invalid file pattern:"));
104        assert!(config_error.to_string().starts_with("Configuration error:"));
105        assert!(processing_error
106            .to_string()
107            .starts_with("Processing error:"));
108    }
109
110    #[test]
111    fn test_error_with_special_characters() {
112        // Test with special characters and Unicode
113        let error = CliError::FileNotFound("ファイル/test 文件.txt".to_string());
114        assert_eq!(error.to_string(), "File not found: ファイル/test 文件.txt");
115
116        let pattern_error = CliError::InvalidPattern("**[!@#$%^&*()".to_string());
117        assert_eq!(
118            pattern_error.to_string(),
119            "Invalid file pattern: **[!@#$%^&*()"
120        );
121    }
122}