Skip to main content

codelens_core/
error.rs

1//! Error types for codelens-core.
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6/// Error type for codelens-core operations.
7#[derive(Error, Debug)]
8pub enum Error {
9    /// Failed to read a file.
10    #[error("failed to read file: {path}")]
11    FileRead {
12        path: PathBuf,
13        #[source]
14        source: std::io::Error,
15    },
16
17    /// Failed to parse configuration file.
18    #[error("failed to parse config file: {path}")]
19    ConfigParse {
20        path: PathBuf,
21        #[source]
22        source: toml::de::Error,
23    },
24
25    /// Invalid language definition.
26    #[error("invalid language definition '{name}': {reason}")]
27    InvalidLanguage { name: String, reason: String },
28
29    /// Invalid regex pattern.
30    #[error("invalid regex pattern: {pattern}")]
31    InvalidRegex {
32        pattern: String,
33        #[source]
34        source: regex::Error,
35    },
36
37    /// Invalid glob pattern.
38    #[error("invalid glob pattern: {pattern}")]
39    InvalidGlob {
40        pattern: String,
41        #[source]
42        source: globset::Error,
43    },
44
45    /// Directory not found.
46    #[error("directory not found: {path}")]
47    DirectoryNotFound { path: PathBuf },
48
49    /// Output write error.
50    #[error("failed to write output")]
51    OutputWrite(#[from] std::io::Error),
52
53    /// Template render error.
54    #[error("failed to render template")]
55    TemplateRender(#[from] askama::Error),
56
57    /// JSON serialization error.
58    #[error("failed to serialize JSON")]
59    JsonSerialize(#[from] serde_json::Error),
60
61    /// Directory traversal error.
62    #[error("directory traversal error: {0}")]
63    Walk(#[from] ignore::Error),
64
65    /// Language definition file parse error.
66    #[error("failed to parse language definitions")]
67    LanguageParse(#[from] toml::de::Error),
68}
69
70/// Result type alias for codelens-core operations.
71pub type Result<T> = std::result::Result<T, Error>;
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use std::io;
77
78    #[test]
79    fn test_error_display_directory_not_found() {
80        let err = Error::DirectoryNotFound {
81            path: PathBuf::from("/nonexistent"),
82        };
83        assert_eq!(err.to_string(), "directory not found: /nonexistent");
84    }
85
86    #[test]
87    fn test_error_display_invalid_language() {
88        let err = Error::InvalidLanguage {
89            name: "Test".to_string(),
90            reason: "missing extensions".to_string(),
91        };
92        assert_eq!(
93            err.to_string(),
94            "invalid language definition 'Test': missing extensions"
95        );
96    }
97
98    #[test]
99    #[allow(clippy::invalid_regex)]
100    fn test_error_display_invalid_regex() {
101        let regex_err = regex::Regex::new("[invalid").unwrap_err();
102        let err = Error::InvalidRegex {
103            pattern: "[invalid".to_string(),
104            source: regex_err,
105        };
106        assert!(err.to_string().contains("invalid regex pattern"));
107    }
108
109    #[test]
110    fn test_error_from_io_error() {
111        let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
112        let err: Error = Error::OutputWrite(io_err);
113        assert!(err.to_string().contains("failed to write output"));
114    }
115
116    #[test]
117    fn test_result_type() {
118        fn returns_ok() -> Result<i32> {
119            Ok(42)
120        }
121        assert_eq!(returns_ok().unwrap(), 42);
122
123        let err: Result<i32> = Err(Error::DirectoryNotFound {
124            path: PathBuf::from("/test"),
125        });
126        assert!(err.is_err());
127    }
128}