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    /// Not a git repository.
66    #[error("not a git repository: {path}")]
67    NotGitRepo { path: PathBuf },
68
69    /// Git command execution failed.
70    #[error("git command failed: {message}")]
71    GitError { message: String },
72
73    /// No snapshots found.
74    #[error("no snapshots found in {path}")]
75    NoSnapshots { path: PathBuf },
76
77    /// Snapshot not found.
78    #[error("snapshot not found: {id}")]
79    SnapshotNotFound { id: String },
80
81    /// Language definition file parse error.
82    #[error("failed to parse language definitions")]
83    LanguageParse(#[from] toml::de::Error),
84}
85
86/// Result type alias for codelens-core operations.
87pub type Result<T> = std::result::Result<T, Error>;
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use std::io;
93
94    #[test]
95    fn test_error_display_directory_not_found() {
96        let err = Error::DirectoryNotFound {
97            path: PathBuf::from("/nonexistent"),
98        };
99        assert_eq!(err.to_string(), "directory not found: /nonexistent");
100    }
101
102    #[test]
103    fn test_error_display_invalid_language() {
104        let err = Error::InvalidLanguage {
105            name: "Test".to_string(),
106            reason: "missing extensions".to_string(),
107        };
108        assert_eq!(
109            err.to_string(),
110            "invalid language definition 'Test': missing extensions"
111        );
112    }
113
114    #[test]
115    #[allow(clippy::invalid_regex)]
116    fn test_error_display_invalid_regex() {
117        let regex_err = regex::Regex::new("[invalid").unwrap_err();
118        let err = Error::InvalidRegex {
119            pattern: "[invalid".to_string(),
120            source: regex_err,
121        };
122        assert!(err.to_string().contains("invalid regex pattern"));
123    }
124
125    #[test]
126    fn test_error_from_io_error() {
127        let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
128        let err: Error = Error::OutputWrite(io_err);
129        assert!(err.to_string().contains("failed to write output"));
130    }
131
132    #[test]
133    fn test_error_display_not_git_repo() {
134        let err = Error::NotGitRepo {
135            path: PathBuf::from("/some/path"),
136        };
137        assert_eq!(err.to_string(), "not a git repository: /some/path");
138    }
139
140    #[test]
141    fn test_error_display_git_error() {
142        let err = Error::GitError {
143            message: "command not found".to_string(),
144        };
145        assert_eq!(err.to_string(), "git command failed: command not found");
146    }
147
148    #[test]
149    fn test_error_display_no_snapshots() {
150        let err = Error::NoSnapshots {
151            path: PathBuf::from(".codelens/snapshots"),
152        };
153        assert_eq!(err.to_string(), "no snapshots found in .codelens/snapshots");
154    }
155
156    #[test]
157    fn test_error_display_snapshot_not_found() {
158        let err = Error::SnapshotNotFound {
159            id: "2026-04-01".to_string(),
160        };
161        assert_eq!(err.to_string(), "snapshot not found: 2026-04-01");
162    }
163
164    #[test]
165    fn test_result_type() {
166        fn returns_ok() -> Result<i32> {
167            Ok(42)
168        }
169        assert_eq!(returns_ok().unwrap(), 42);
170
171        let err: Result<i32> = Err(Error::DirectoryNotFound {
172            path: PathBuf::from("/test"),
173        });
174        assert!(err.is_err());
175    }
176}