/home/b/lab/denet/src/error.rs
Line | Count | Source |
1 | | //! Error types for the denet library |
2 | | //! |
3 | | //! This module provides comprehensive error handling for all denet operations, |
4 | | //! including process monitoring, I/O operations, and system interactions. |
5 | | |
6 | | use std::fmt; |
7 | | |
8 | | /// Main error type for denet operations |
9 | | #[derive(Debug)] |
10 | | pub enum DenetError { |
11 | | /// I/O related errors (file operations, network, etc.) |
12 | | Io(std::io::Error), |
13 | | /// Process not found or inaccessible |
14 | | ProcessNotFound(usize), |
15 | | /// Process access denied |
16 | | ProcessAccessDenied(usize), |
17 | | /// System time errors |
18 | | SystemTime(std::time::SystemTimeError), |
19 | | /// JSON serialization/deserialization errors |
20 | | Serialization(serde_json::Error), |
21 | | /// Configuration or parameter validation errors |
22 | | InvalidConfiguration(String), |
23 | | /// Platform-specific operation not supported |
24 | | PlatformNotSupported(String), |
25 | | /// eBPF initialization or operation errors |
26 | | EbpfInitError(String), |
27 | | /// eBPF not supported on this platform |
28 | | EbpfNotSupported(String), |
29 | | /// Generic error with message |
30 | | Other(String), |
31 | | } |
32 | | |
33 | | impl fmt::Display for DenetError { |
34 | 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
35 | 12 | match self { |
36 | 2 | DenetError::Io(err) => write!(f, "I/O error: {err}"), |
37 | 2 | DenetError::ProcessNotFound(pid) => write!(f, "Process not found: {pid}"), |
38 | 1 | DenetError::ProcessAccessDenied(pid) => write!(f, "Access denied for process: {pid}"), |
39 | 1 | DenetError::SystemTime(err) => write!(f, "System time error: {err}"), |
40 | 1 | DenetError::Serialization(err) => write!(f, "Serialization error: {err}"), |
41 | 1 | DenetError::InvalidConfiguration(msg) => write!(f, "Invalid configuration: {msg}"), |
42 | 1 | DenetError::PlatformNotSupported(msg) => write!(f, "Platform not supported: {msg}"), |
43 | 1 | DenetError::EbpfInitError(msg) => write!(f, "eBPF initialization error: {msg}"), |
44 | 1 | DenetError::EbpfNotSupported(msg) => write!(f, "eBPF not supported: {msg}"), |
45 | 1 | DenetError::Other(msg) => write!(f, "Error: {msg}"), |
46 | | } |
47 | 12 | } |
48 | | } |
49 | | |
50 | | impl std::error::Error for DenetError { |
51 | 10 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
52 | 10 | match *self { |
53 | 1 | DenetError::Io(ref err) => Some(err), |
54 | 1 | DenetError::SystemTime(ref err) => Some(err), |
55 | 1 | DenetError::Serialization(ref err) => Some(err), |
56 | 7 | _ => None, |
57 | | } |
58 | 10 | } |
59 | | } |
60 | | |
61 | | // Conversions from standard library errors |
62 | | impl From<std::io::Error> for DenetError { |
63 | 1 | fn from(err: std::io::Error) -> Self { |
64 | 1 | DenetError::Io(err) |
65 | 1 | } |
66 | | } |
67 | | |
68 | | impl From<std::time::SystemTimeError> for DenetError { |
69 | 1 | fn from(err: std::time::SystemTimeError) -> Self { |
70 | 1 | DenetError::SystemTime(err) |
71 | 1 | } |
72 | | } |
73 | | |
74 | | impl From<serde_json::Error> for DenetError { |
75 | 1 | fn from(err: serde_json::Error) -> Self { |
76 | 1 | DenetError::Serialization(err) |
77 | 1 | } |
78 | | } |
79 | | |
80 | | // Additional conversions for compatibility |
81 | | impl From<DenetError> for std::io::Error { |
82 | 2 | fn from(err: DenetError) -> Self { |
83 | 2 | match err { |
84 | 1 | DenetError::Io(io_err) => io_err, |
85 | 1 | _ => std::io::Error::other(err.to_string()), |
86 | | } |
87 | 2 | } |
88 | | } |
89 | | |
90 | | /// Convenience type alias for Results with DenetError |
91 | | pub type Result<T> = std::result::Result<T, DenetError>; |
92 | | |
93 | | /// Convert DenetError to PyO3 error for Python bindings |
94 | | #[cfg(feature = "python")] |
95 | | impl From<DenetError> for pyo3::PyErr { |
96 | | fn from(err: DenetError) -> Self { |
97 | | use pyo3::exceptions::*; |
98 | | match err { |
99 | | DenetError::Io(_) => PyIOError::new_err(err.to_string()), |
100 | | DenetError::ProcessNotFound(_) => PyRuntimeError::new_err(err.to_string()), |
101 | | DenetError::ProcessAccessDenied(_) => PyPermissionError::new_err(err.to_string()), |
102 | | DenetError::InvalidConfiguration(_) => PyValueError::new_err(err.to_string()), |
103 | | DenetError::PlatformNotSupported(_) => PyNotImplementedError::new_err(err.to_string()), |
104 | | DenetError::EbpfInitError(_) => PyRuntimeError::new_err(err.to_string()), |
105 | | DenetError::EbpfNotSupported(_) => PyNotImplementedError::new_err(err.to_string()), |
106 | | _ => PyRuntimeError::new_err(err.to_string()), |
107 | | } |
108 | | } |
109 | | } |
110 | | |
111 | | #[cfg(test)] |
112 | | mod tests { |
113 | | use super::*; |
114 | | use std::error::Error; |
115 | | use std::io; |
116 | | |
117 | | #[test] |
118 | 1 | fn test_denet_error_display() { |
119 | 1 | // Test IO error display |
120 | 1 | let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found"); |
121 | 1 | let denet_err = DenetError::Io(io_err); |
122 | 1 | assert!(denet_err.to_string().contains("I/O error")); |
123 | 1 | assert!(denet_err.to_string().contains("file not found")); |
124 | | |
125 | | // Test process not found error |
126 | 1 | let pid = 12345; |
127 | 1 | let err = DenetError::ProcessNotFound(pid); |
128 | 1 | assert_eq!(err.to_string(), format!("Process not found: {}", pid)); |
129 | | |
130 | | // Test process access denied error |
131 | 1 | let err = DenetError::ProcessAccessDenied(pid); |
132 | 1 | assert_eq!( |
133 | 1 | err.to_string(), |
134 | 1 | format!("Access denied for process: {}", pid) |
135 | 1 | ); |
136 | | |
137 | | // Test system time error |
138 | 1 | let time_err = std::time::SystemTime::now() |
139 | 1 | .duration_since(std::time::SystemTime::now() + std::time::Duration::from_secs(1)) |
140 | 1 | .unwrap_err(); |
141 | 1 | let err = DenetError::SystemTime(time_err); |
142 | 1 | assert!(err.to_string().contains("System time error")); |
143 | | |
144 | | // Test serialization error |
145 | 1 | let json_err = serde_json::Error::io(io::Error::new(io::ErrorKind::Other, "JSON error")); |
146 | 1 | let err = DenetError::Serialization(json_err); |
147 | 1 | assert!(err.to_string().contains("Serialization error")); |
148 | | |
149 | | // Test invalid configuration error |
150 | 1 | let msg = "Invalid config parameter"; |
151 | 1 | let err = DenetError::InvalidConfiguration(msg.to_string()); |
152 | 1 | assert_eq!(err.to_string(), format!("Invalid configuration: {}", msg)); |
153 | | |
154 | | // Test platform not supported error |
155 | 1 | let msg = "Windows feature"; |
156 | 1 | let err = DenetError::PlatformNotSupported(msg.to_string()); |
157 | 1 | assert_eq!(err.to_string(), format!("Platform not supported: {}", msg)); |
158 | | |
159 | | // Test eBPF initialization error |
160 | 1 | let msg = "Failed to initialize BPF"; |
161 | 1 | let err = DenetError::EbpfInitError(msg.to_string()); |
162 | 1 | assert_eq!( |
163 | 1 | err.to_string(), |
164 | 1 | format!("eBPF initialization error: {}", msg) |
165 | 1 | ); |
166 | | |
167 | | // Test eBPF not supported error |
168 | 1 | let msg = "Kernel too old"; |
169 | 1 | let err = DenetError::EbpfNotSupported(msg.to_string()); |
170 | 1 | assert_eq!(err.to_string(), format!("eBPF not supported: {}", msg)); |
171 | | |
172 | | // Test other error |
173 | 1 | let msg = "Generic error"; |
174 | 1 | let err = DenetError::Other(msg.to_string()); |
175 | 1 | assert_eq!(err.to_string(), format!("Error: {}", msg)); |
176 | 1 | } |
177 | | |
178 | | #[test] |
179 | 1 | fn test_denet_error_source() { |
180 | 1 | // Test IO error source |
181 | 1 | let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found"); |
182 | 1 | let denet_err = DenetError::Io(io_err); |
183 | 1 | assert!(denet_err.source().is_some()); |
184 | | |
185 | | // Test system time error source |
186 | 1 | let time_err = std::time::SystemTime::now() |
187 | 1 | .duration_since(std::time::SystemTime::now() + std::time::Duration::from_secs(1)) |
188 | 1 | .unwrap_err(); |
189 | 1 | let denet_err = DenetError::SystemTime(time_err); |
190 | 1 | assert!(denet_err.source().is_some()); |
191 | | |
192 | | // Test serialization error source |
193 | 1 | let json_err = serde_json::Error::io(io::Error::new(io::ErrorKind::Other, "JSON error")); |
194 | 1 | let denet_err = DenetError::Serialization(json_err); |
195 | 1 | assert!(denet_err.source().is_some()); |
196 | | |
197 | | // Test errors without source |
198 | 1 | assert!(DenetError::ProcessNotFound(123).source().is_none()); |
199 | 1 | assert!(DenetError::ProcessAccessDenied(456).source().is_none()); |
200 | 1 | assert!(DenetError::InvalidConfiguration("test".to_string()) |
201 | 1 | .source() |
202 | 1 | .is_none()); |
203 | 1 | assert!(DenetError::PlatformNotSupported("test".to_string()) |
204 | 1 | .source() |
205 | 1 | .is_none()); |
206 | 1 | assert!(DenetError::EbpfInitError("test".to_string()) |
207 | 1 | .source() |
208 | 1 | .is_none()); |
209 | 1 | assert!(DenetError::EbpfNotSupported("test".to_string()) |
210 | 1 | .source() |
211 | 1 | .is_none()); |
212 | 1 | assert!(DenetError::Other("test".to_string()).source().is_none()); |
213 | 1 | } |
214 | | |
215 | | #[test] |
216 | 1 | fn test_error_conversions_from_std() { |
217 | 1 | // Test From<io::Error> |
218 | 1 | let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found"); |
219 | 1 | let denet_err: DenetError = io_err.into(); |
220 | 1 | match denet_err { |
221 | 1 | DenetError::Io(_) => (), |
222 | 0 | _ => panic!("Expected Io error variant"), |
223 | | } |
224 | | |
225 | | // Test From<SystemTimeError> |
226 | 1 | let time_err = std::time::SystemTime::now() |
227 | 1 | .duration_since(std::time::SystemTime::now() + std::time::Duration::from_secs(1)) |
228 | 1 | .unwrap_err(); |
229 | 1 | let denet_err: DenetError = time_err.into(); |
230 | 1 | match denet_err { |
231 | 1 | DenetError::SystemTime(_) => (), |
232 | 0 | _ => panic!("Expected SystemTime error variant"), |
233 | | } |
234 | | |
235 | | // Test From<serde_json::Error> |
236 | 1 | let json_err = serde_json::Error::io(io::Error::new(io::ErrorKind::Other, "JSON error")); |
237 | 1 | let denet_err: DenetError = json_err.into(); |
238 | 1 | match denet_err { |
239 | 1 | DenetError::Serialization(_) => (), |
240 | 0 | _ => panic!("Expected Serialization error variant"), |
241 | | } |
242 | 1 | } |
243 | | |
244 | | #[test] |
245 | 1 | fn test_error_conversion_to_io_error() { |
246 | 1 | // Test From<DenetError> for io::Error - IO variant |
247 | 1 | let original_io_err = io::Error::new(io::ErrorKind::NotFound, "original io error"); |
248 | 1 | let denet_err = DenetError::Io(original_io_err); |
249 | 1 | let converted_io_err: io::Error = denet_err.into(); |
250 | 1 | assert!(converted_io_err.to_string().contains("original io error")); |
251 | | |
252 | | // Test From<DenetError> for io::Error - non-IO variant |
253 | 1 | let original_err = DenetError::ProcessNotFound(123); |
254 | 1 | let io_err: io::Error = original_err.into(); |
255 | 1 | assert!(io_err.to_string().contains("Process not found")); |
256 | 1 | } |
257 | | |
258 | | #[test] |
259 | 1 | fn test_result_type_alias() { |
260 | 1 | // Test with success |
261 | 1 | let result: Result<i32> = Ok(42); |
262 | 1 | assert_eq!(result.unwrap(), 42); |
263 | | |
264 | | // Test with error |
265 | 1 | let error_result: Result<i32> = Err(DenetError::Other("test error".to_string())); |
266 | 1 | assert!(error_result.is_err()); |
267 | 1 | match error_result { |
268 | 1 | Err(DenetError::Other(msg)) => assert_eq!(msg, "test error"), |
269 | 0 | _ => panic!("Expected Other error variant"), |
270 | | } |
271 | 1 | } |
272 | | |
273 | | #[test] |
274 | 1 | fn test_denet_error_debug() { |
275 | 1 | let err = DenetError::ProcessNotFound(123); |
276 | 1 | let debug_str = format!("{:?}", err); |
277 | 1 | assert!(debug_str.contains("ProcessNotFound")); |
278 | 1 | assert!(debug_str.contains("123")); |
279 | 1 | } |
280 | | |
281 | | #[cfg(feature = "python")] |
282 | | #[test] |
283 | | fn test_python_error_conversion() { |
284 | | use pyo3::exceptions::*; |
285 | | use pyo3::Python; |
286 | | |
287 | | Python::with_gil(|py| { |
288 | | // Test IO error conversion |
289 | | let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found"); |
290 | | let denet_err = DenetError::Io(io_err); |
291 | | let py_err: pyo3::PyErr = denet_err.into(); |
292 | | assert!(py_err.is_instance_of::<PyIOError>(py)); |
293 | | |
294 | | // Test ProcessNotFound conversion |
295 | | let denet_err = DenetError::ProcessNotFound(123); |
296 | | let py_err: pyo3::PyErr = denet_err.into(); |
297 | | assert!(py_err.is_instance_of::<PyRuntimeError>(py)); |
298 | | |
299 | | // Test ProcessAccessDenied conversion |
300 | | let denet_err = DenetError::ProcessAccessDenied(123); |
301 | | let py_err: pyo3::PyErr = denet_err.into(); |
302 | | assert!(py_err.is_instance_of::<PyPermissionError>(py)); |
303 | | |
304 | | // Test InvalidConfiguration conversion |
305 | | let denet_err = DenetError::InvalidConfiguration("test".to_string()); |
306 | | let py_err: pyo3::PyErr = denet_err.into(); |
307 | | assert!(py_err.is_instance_of::<PyValueError>(py)); |
308 | | |
309 | | // Test PlatformNotSupported conversion |
310 | | let denet_err = DenetError::PlatformNotSupported("test".to_string()); |
311 | | let py_err: pyo3::PyErr = denet_err.into(); |
312 | | assert!(py_err.is_instance_of::<PyNotImplementedError>(py)); |
313 | | |
314 | | // Test EbpfInitError conversion |
315 | | let denet_err = DenetError::EbpfInitError("test".to_string()); |
316 | | let py_err: pyo3::PyErr = denet_err.into(); |
317 | | assert!(py_err.is_instance_of::<PyRuntimeError>(py)); |
318 | | |
319 | | // Test EbpfNotSupported conversion |
320 | | let denet_err = DenetError::EbpfNotSupported("test".to_string()); |
321 | | let py_err: pyo3::PyErr = denet_err.into(); |
322 | | assert!(py_err.is_instance_of::<PyNotImplementedError>(py)); |
323 | | |
324 | | // Test Other error conversion (fallback) |
325 | | let denet_err = DenetError::Other("test".to_string()); |
326 | | let py_err: pyo3::PyErr = denet_err.into(); |
327 | | assert!(py_err.is_instance_of::<PyRuntimeError>(py)); |
328 | | |
329 | | // Test SystemTime error conversion (fallback) |
330 | | let time_err = std::time::SystemTime::now() |
331 | | .duration_since(std::time::SystemTime::now() + std::time::Duration::from_secs(1)) |
332 | | .unwrap_err(); |
333 | | let denet_err = DenetError::SystemTime(time_err); |
334 | | let py_err: pyo3::PyErr = denet_err.into(); |
335 | | assert!(py_err.is_instance_of::<PyRuntimeError>(py)); |
336 | | |
337 | | // Test Serialization error conversion (fallback) |
338 | | let json_err = |
339 | | serde_json::Error::io(io::Error::new(io::ErrorKind::Other, "JSON error")); |
340 | | let denet_err = DenetError::Serialization(json_err); |
341 | | let py_err: pyo3::PyErr = denet_err.into(); |
342 | | assert!(py_err.is_instance_of::<PyRuntimeError>(py)); |
343 | | }); |
344 | | } |
345 | | } |