1use crate::hooks::HookError;
2use crate::malware_db::MalwareDbError;
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum AuditError {
7 #[error("File not found: {0}")]
8 FileNotFound(String),
9
10 #[error("Failed to read file: {path}")]
11 ReadError {
12 path: String,
13 #[source]
14 source: std::io::Error,
15 },
16
17 #[error("Failed to parse YAML frontmatter: {path}")]
18 YamlParseError {
19 path: String,
20 #[source]
21 source: serde_yaml::Error,
22 },
23
24 #[error("Invalid SKILL.md format: {0}")]
25 InvalidSkillFormat(String),
26
27 #[error("Regex compilation error: {0}")]
28 RegexError(#[from] regex::Error),
29
30 #[error("Path is not a directory: {0}")]
31 NotADirectory(String),
32
33 #[error("JSON serialization error: {0}")]
34 JsonError(#[from] serde_json::Error),
35
36 #[error("Failed to parse file: {path} - {message}")]
37 ParseError { path: String, message: String },
38
39 #[error("Hook operation failed: {0}")]
40 Hook(#[from] HookError),
41
42 #[error("Malware database error: {0}")]
43 MalwareDb(#[from] MalwareDbError),
44
45 #[error("File watch error: {0}")]
46 Watch(#[from] notify::Error),
47
48 #[error("Configuration error: {0}")]
49 Config(String),
50}
51
52pub type Result<T> = std::result::Result<T, AuditError>;
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn test_error_display_file_not_found() {
60 let err = AuditError::FileNotFound("/path/to/file".to_string());
61 assert_eq!(err.to_string(), "File not found: /path/to/file");
62 }
63
64 #[test]
65 fn test_error_display_read_error() {
66 let err = AuditError::ReadError {
67 path: "/path/to/file".to_string(),
68 source: std::io::Error::new(std::io::ErrorKind::NotFound, "not found"),
69 };
70 assert_eq!(err.to_string(), "Failed to read file: /path/to/file");
71 }
72
73 #[test]
74 fn test_error_display_invalid_skill_format() {
75 let err = AuditError::InvalidSkillFormat("missing frontmatter".to_string());
76 assert_eq!(
77 err.to_string(),
78 "Invalid SKILL.md format: missing frontmatter"
79 );
80 }
81
82 #[test]
83 fn test_error_display_not_a_directory() {
84 let err = AuditError::NotADirectory("/path/to/file".to_string());
85 assert_eq!(err.to_string(), "Path is not a directory: /path/to/file");
86 }
87
88 #[test]
89 fn test_error_display_parse_error() {
90 let err = AuditError::ParseError {
91 path: "/path/to/file".to_string(),
92 message: "invalid JSON".to_string(),
93 };
94 assert_eq!(
95 err.to_string(),
96 "Failed to parse file: /path/to/file - invalid JSON"
97 );
98 }
99
100 #[test]
101 fn test_error_from_hook_error() {
102 let hook_error = HookError::NotAGitRepository;
103 let err: AuditError = hook_error.into();
104 assert!(err.to_string().contains("Hook operation failed"));
105 }
106
107 #[test]
108 fn test_error_from_malware_db_error() {
109 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "not found");
110 let malware_error = MalwareDbError::ReadFile(io_error);
111 let err: AuditError = malware_error.into();
112 assert!(err.to_string().contains("Malware database error"));
113 }
114
115 #[test]
116 fn test_error_display_config() {
117 let err = AuditError::Config("invalid value".to_string());
118 assert_eq!(err.to_string(), "Configuration error: invalid value");
119 }
120}