usn_journal_rs/
errors.rs

1//! This module defines the custom error types.
2
3use thiserror::Error;
4
5/// Custom error type for USN Journal and MFT operations.
6#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum UsnError {
9    #[error("Access denied: Administrator privileges required.")]
10    PermissionError,
11
12    #[error("Invalid mount point: {0}")]
13    InvalidMountPointError(String),
14
15    #[error("IO error: {0}")]
16    IoError(#[from] std::io::Error),
17
18    #[error("Windows API error: {0}")]
19    WinApiError(#[from] windows::core::Error),
20
21    #[error("Other error: {0}")]
22    OtherError(String),
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28    use std::io::{Error as IoError, ErrorKind};
29    use windows::Win32::Foundation::ERROR_ACCESS_DENIED;
30
31    // Unit tests for UsnError variants and behavior
32    mod error_variant_tests {
33        use super::*;
34
35        #[test]
36        fn test_permission_error_display() {
37            let error = UsnError::PermissionError;
38            let error_string = error.to_string();
39            assert_eq!(
40                error_string,
41                "Access denied: Administrator privileges required."
42            );
43        }
44
45        #[test]
46        fn test_invalid_mount_point_error_display() {
47            let mount_point = "C:\\invalid\\path";
48            let error = UsnError::InvalidMountPointError(mount_point.to_string());
49            let error_string = error.to_string();
50            assert_eq!(error_string, "Invalid mount point: C:\\invalid\\path");
51        }
52
53        #[test]
54        fn test_other_error_display() {
55            let message = "Custom error message";
56            let error = UsnError::OtherError(message.to_string());
57            let error_string = error.to_string();
58            assert_eq!(error_string, "Other error: Custom error message");
59        }
60
61        #[test]
62        fn test_io_error_conversion() {
63            let io_error = IoError::new(ErrorKind::NotFound, "File not found");
64            let usn_error = UsnError::from(io_error);
65
66            match usn_error {
67                UsnError::IoError(ref e) => {
68                    assert_eq!(e.kind(), ErrorKind::NotFound);
69                    assert_eq!(e.to_string(), "File not found");
70                }
71                _ => panic!("Expected IoError variant"),
72            }
73        }
74
75        #[test]
76        fn test_windows_error_conversion() {
77            let win_error = windows::core::Error::from(ERROR_ACCESS_DENIED);
78            let usn_error = UsnError::from(win_error);
79
80            match usn_error {
81                UsnError::WinApiError(ref e) => {
82                    assert_eq!(e.code(), ERROR_ACCESS_DENIED.into());
83                }
84                _ => panic!("Expected WinApiError variant"),
85            }
86        }
87
88        #[test]
89        fn test_error_chain_display() {
90            let io_error = IoError::new(ErrorKind::PermissionDenied, "Access denied");
91            let usn_error = UsnError::from(io_error);
92            let error_string = usn_error.to_string();
93            assert!(error_string.contains("IO error:"));
94            assert!(error_string.contains("Access denied"));
95        }
96    }
97
98    // Tests for error matching and handling patterns
99    mod error_handling_tests {
100        use super::*;
101
102        #[test]
103        fn test_result_type_integration() {
104            // Test that UsnError works correctly with Result types
105            fn returns_permission_error() -> Result<(), UsnError> {
106                Err(UsnError::PermissionError)
107            }
108
109            fn returns_ok() -> Result<String, UsnError> {
110                Ok("success".to_string())
111            }
112
113            assert!(returns_permission_error().is_err());
114            assert!(returns_ok().is_ok());
115            assert_eq!(returns_ok().unwrap(), "success");
116        }
117
118        #[test]
119        fn test_error_source_chain() {
120            use std::error::Error;
121
122            let io_error = IoError::new(ErrorKind::NotFound, "Original error");
123            let usn_error = UsnError::from(io_error);
124
125            // Test that the source chain is preserved
126            assert!(usn_error.source().is_some());
127            if let UsnError::IoError(ref e) = usn_error {
128                assert_eq!(e.to_string(), "Original error");
129            }
130        }
131    }
132
133    // Tests for specific error scenarios common in USN operations
134    mod usn_specific_error_tests {
135        use super::*;
136
137        #[test]
138        fn test_common_permission_scenarios() {
139            // Test that permission errors have the expected message
140            let error = UsnError::PermissionError;
141            assert!(
142                error
143                    .to_string()
144                    .contains("Administrator privileges required")
145            );
146        }
147
148        #[test]
149        fn test_mount_point_error_scenarios() {
150            let invalid_paths = vec![
151                "Z:\\nonexistent",
152                "\\\\invalid\\unc\\path",
153                "C:\\path\\that\\does\\not\\exist",
154                "",
155            ];
156
157            for path in invalid_paths {
158                let error = UsnError::InvalidMountPointError(path.to_string());
159                assert!(error.to_string().contains("Invalid mount point:"));
160                assert!(error.to_string().contains(path));
161            }
162        }
163
164        #[test]
165        fn test_windows_api_error_codes() {
166            use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_HANDLE};
167
168            let error_codes = vec![
169                ERROR_ACCESS_DENIED,
170                ERROR_FILE_NOT_FOUND,
171                ERROR_INVALID_HANDLE,
172            ];
173
174            for code in error_codes {
175                let win_error = windows::core::Error::from(code);
176                let usn_error = UsnError::from(win_error);
177
178                if let UsnError::WinApiError(ref e) = usn_error {
179                    assert_eq!(e.code(), code.into());
180                } else {
181                    panic!("Expected WinApiError variant");
182                }
183            }
184        }
185    }
186}