1use std::path::PathBuf;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum Error {
9 #[error("failed to read file: {path}")]
11 FileRead {
12 path: PathBuf,
13 #[source]
14 source: std::io::Error,
15 },
16
17 #[error("failed to parse config file: {path}")]
19 ConfigParse {
20 path: PathBuf,
21 #[source]
22 source: toml::de::Error,
23 },
24
25 #[error("invalid language definition '{name}': {reason}")]
27 InvalidLanguage { name: String, reason: String },
28
29 #[error("invalid regex pattern: {pattern}")]
31 InvalidRegex {
32 pattern: String,
33 #[source]
34 source: regex::Error,
35 },
36
37 #[error("invalid glob pattern: {pattern}")]
39 InvalidGlob {
40 pattern: String,
41 #[source]
42 source: globset::Error,
43 },
44
45 #[error("directory not found: {path}")]
47 DirectoryNotFound { path: PathBuf },
48
49 #[error("failed to write output")]
51 OutputWrite(#[from] std::io::Error),
52
53 #[error("failed to render template")]
55 TemplateRender(#[from] askama::Error),
56
57 #[error("failed to serialize JSON")]
59 JsonSerialize(#[from] serde_json::Error),
60
61 #[error("directory traversal error: {0}")]
63 Walk(#[from] ignore::Error),
64
65 #[error("failed to parse language definitions")]
67 LanguageParse(#[from] toml::de::Error),
68}
69
70pub 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}