1use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum OracleError {
8 #[error("Failed to load patterns from {path}: {source}")]
10 LoadError {
11 path: String,
12 #[source]
13 source: std::io::Error,
14 },
15
16 #[error("Failed to save patterns to {path}: {source}")]
18 SaveError {
19 path: String,
20 #[source]
21 source: std::io::Error,
22 },
23
24 #[error("Invalid pattern format: {0}")]
26 InvalidPattern(String),
27
28 #[error("Pattern store error: {0}")]
30 PatternStoreError(String),
31
32 #[error("Configuration error: {0}")]
34 ConfigError(String),
35
36 #[error("Failed to apply diff: {0}")]
38 DiffError(String),
39
40 #[error("Export error: {0}")]
42 ExportError(String),
43}
44
45impl From<std::io::Error> for OracleError {
46 fn from(e: std::io::Error) -> Self {
47 Self::LoadError { path: "<unknown>".into(), source: e }
48 }
49}
50
51impl From<toml::de::Error> for OracleError {
52 fn from(e: toml::de::Error) -> Self {
53 Self::ConfigError(e.to_string())
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
66 fn test_load_error_construction() {
67 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
68 let err = OracleError::LoadError { path: "/tmp/test.apr".into(), source: io_err };
69 assert!(matches!(err, OracleError::LoadError { .. }));
70 }
71
72 #[test]
73 fn test_save_error_construction() {
74 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
75 let err = OracleError::SaveError { path: "/etc/test.apr".into(), source: io_err };
76 assert!(matches!(err, OracleError::SaveError { .. }));
77 }
78
79 #[test]
80 fn test_invalid_pattern_error() {
81 let err = OracleError::InvalidPattern("malformed pattern syntax".into());
82 assert!(matches!(err, OracleError::InvalidPattern(_)));
83 }
84
85 #[test]
86 fn test_pattern_store_error() {
87 let err = OracleError::PatternStoreError("store corruption detected".into());
88 assert!(matches!(err, OracleError::PatternStoreError(_)));
89 }
90
91 #[test]
92 fn test_config_error() {
93 let err = OracleError::ConfigError("invalid threshold value".into());
94 assert!(matches!(err, OracleError::ConfigError(_)));
95 }
96
97 #[test]
98 fn test_diff_error() {
99 let err = OracleError::DiffError("hunk mismatch at line 42".into());
100 assert!(matches!(err, OracleError::DiffError(_)));
101 }
102
103 #[test]
104 fn test_export_error() {
105 let err = OracleError::ExportError("failed to write parquet".into());
106 assert!(matches!(err, OracleError::ExportError(_)));
107 }
108
109 #[test]
114 fn test_load_error_display() {
115 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
116 let err = OracleError::LoadError { path: "/tmp/patterns.apr".into(), source: io_err };
117 let msg = format!("{}", err);
118 assert!(msg.contains("/tmp/patterns.apr"));
119 assert!(msg.contains("file not found"));
120 }
121
122 #[test]
123 fn test_save_error_display() {
124 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
125 let err = OracleError::SaveError { path: "/etc/patterns.apr".into(), source: io_err };
126 let msg = format!("{}", err);
127 assert!(msg.contains("/etc/patterns.apr"));
128 assert!(msg.contains("access denied"));
129 }
130
131 #[test]
132 fn test_invalid_pattern_display() {
133 let err = OracleError::InvalidPattern("missing error_code field".into());
134 let msg = format!("{}", err);
135 assert!(msg.contains("Invalid pattern format"));
136 assert!(msg.contains("missing error_code field"));
137 }
138
139 #[test]
140 fn test_pattern_store_error_display() {
141 let err = OracleError::PatternStoreError("index corruption".into());
142 let msg = format!("{}", err);
143 assert!(msg.contains("Pattern store error"));
144 assert!(msg.contains("index corruption"));
145 }
146
147 #[test]
148 fn test_config_error_display() {
149 let err = OracleError::ConfigError("invalid TOML syntax".into());
150 let msg = format!("{}", err);
151 assert!(msg.contains("Configuration error"));
152 assert!(msg.contains("invalid TOML syntax"));
153 }
154
155 #[test]
156 fn test_diff_error_display() {
157 let err = OracleError::DiffError("context mismatch".into());
158 let msg = format!("{}", err);
159 assert!(msg.contains("Failed to apply diff"));
160 assert!(msg.contains("context mismatch"));
161 }
162
163 #[test]
164 fn test_export_error_display() {
165 let err = OracleError::ExportError("arrow schema error".into());
166 let msg = format!("{}", err);
167 assert!(msg.contains("Export error"));
168 assert!(msg.contains("arrow schema error"));
169 }
170
171 #[test]
176 fn test_from_io_error() {
177 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "no such file");
178 let oracle_err: OracleError = io_err.into();
179
180 match oracle_err {
181 OracleError::LoadError { path, source } => {
182 assert_eq!(path, "<unknown>");
183 assert_eq!(source.kind(), std::io::ErrorKind::NotFound);
184 }
185 _ => panic!("Expected LoadError variant"),
186 }
187 }
188
189 #[test]
190 fn test_from_io_error_preserves_error_kind() {
191 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
192 let oracle_err: OracleError = io_err.into();
193
194 if let OracleError::LoadError { source, .. } = oracle_err {
195 assert_eq!(source.kind(), std::io::ErrorKind::PermissionDenied);
196 } else {
197 panic!("Expected LoadError variant");
198 }
199 }
200
201 #[test]
202 fn test_from_toml_error() {
203 let toml_result: Result<toml::Value, _> = toml::from_str("invalid = [toml");
204 let toml_err = toml_result.unwrap_err();
205 let oracle_err: OracleError = toml_err.into();
206
207 match oracle_err {
208 OracleError::ConfigError(msg) => {
209 assert!(!msg.is_empty());
210 }
211 _ => panic!("Expected ConfigError variant"),
212 }
213 }
214
215 #[test]
220 fn test_debug_formatting() {
221 let err = OracleError::InvalidPattern("test".into());
222 let debug_str = format!("{:?}", err);
223 assert!(debug_str.contains("InvalidPattern"));
224 }
225
226 #[test]
231 fn test_load_error_source_chain() {
232 use std::error::Error;
233
234 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "underlying error");
235 let err = OracleError::LoadError { path: "/test".into(), source: io_err };
236
237 assert!(err.source().is_some());
239 }
240
241 #[test]
242 fn test_save_error_source_chain() {
243 use std::error::Error;
244
245 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk full");
246 let err = OracleError::SaveError { path: "/test".into(), source: io_err };
247
248 assert!(err.source().is_some());
249 }
250
251 #[test]
252 fn test_simple_errors_no_source() {
253 use std::error::Error;
254
255 let err = OracleError::InvalidPattern("test".into());
256 assert!(err.source().is_none());
257
258 let err = OracleError::PatternStoreError("test".into());
259 assert!(err.source().is_none());
260
261 let err = OracleError::ConfigError("test".into());
262 assert!(err.source().is_none());
263
264 let err = OracleError::DiffError("test".into());
265 assert!(err.source().is_none());
266
267 let err = OracleError::ExportError("test".into());
268 assert!(err.source().is_none());
269 }
270}