oxidize_pdf/
error.rs

1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum PdfError {
5    #[error("IO error: {0}")]
6    Io(#[from] std::io::Error),
7
8    #[error("Invalid PDF structure: {0}")]
9    InvalidStructure(String),
10
11    #[error("Invalid object reference: {0}")]
12    InvalidReference(String),
13
14    #[error("Encoding error: {0}")]
15    EncodingError(String),
16
17    #[error("Font error: {0}")]
18    FontError(String),
19
20    #[error("Compression error: {0}")]
21    CompressionError(String),
22
23    #[error("Invalid image: {0}")]
24    InvalidImage(String),
25}
26
27pub type Result<T> = std::result::Result<T, PdfError>;
28
29// Separate error type for oxidize-pdf-core
30#[derive(Error, Debug)]
31pub enum OxidizePdfError {
32    #[error("IO error: {0}")]
33    Io(#[from] std::io::Error),
34
35    #[error("Parse error: {0}")]
36    ParseError(String),
37
38    #[error("Invalid PDF structure: {0}")]
39    InvalidStructure(String),
40
41    #[error("Encoding error: {0}")]
42    EncodingError(String),
43
44    #[error("Other error: {0}")]
45    Other(String),
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use std::io::{Error as IoError, ErrorKind};
52
53    #[test]
54    fn test_pdf_error_display() {
55        let error = PdfError::InvalidStructure("test message".to_string());
56        assert_eq!(error.to_string(), "Invalid PDF structure: test message");
57    }
58
59    #[test]
60    fn test_pdf_error_debug() {
61        let error = PdfError::InvalidReference("object 1 0".to_string());
62        let debug_str = format!("{:?}", error);
63        assert!(debug_str.contains("InvalidReference"));
64        assert!(debug_str.contains("object 1 0"));
65    }
66
67    #[test]
68    fn test_pdf_error_from_io_error() {
69        let io_error = IoError::new(ErrorKind::NotFound, "file not found");
70        let pdf_error = PdfError::from(io_error);
71        
72        match pdf_error {
73            PdfError::Io(ref err) => {
74                assert_eq!(err.kind(), ErrorKind::NotFound);
75            }
76            _ => panic!("Expected IO error variant"),
77        }
78    }
79
80    #[test]
81    fn test_all_pdf_error_variants() {
82        let errors = vec![
83            PdfError::InvalidStructure("structure error".to_string()),
84            PdfError::InvalidReference("ref error".to_string()),
85            PdfError::EncodingError("encoding error".to_string()),
86            PdfError::FontError("font error".to_string()),
87            PdfError::CompressionError("compression error".to_string()),
88            PdfError::InvalidImage("image error".to_string()),
89        ];
90
91        // Test that all variants can be created and displayed
92        for error in errors {
93            let error_string = error.to_string();
94            assert!(!error_string.is_empty());
95            assert!(error_string.contains("error"));
96        }
97    }
98
99    #[test]
100    fn test_oxidize_pdf_error_display() {
101        let error = OxidizePdfError::ParseError("parsing failed".to_string());
102        assert_eq!(error.to_string(), "Parse error: parsing failed");
103    }
104
105    #[test]
106    fn test_oxidize_pdf_error_debug() {
107        let error = OxidizePdfError::InvalidStructure("malformed PDF".to_string());
108        let debug_str = format!("{:?}", error);
109        assert!(debug_str.contains("InvalidStructure"));
110        assert!(debug_str.contains("malformed PDF"));
111    }
112
113    #[test]
114    fn test_oxidize_pdf_error_from_io_error() {
115        let io_error = IoError::new(ErrorKind::PermissionDenied, "access denied");
116        let pdf_error = OxidizePdfError::from(io_error);
117        
118        match pdf_error {
119            OxidizePdfError::Io(ref err) => {
120                assert_eq!(err.kind(), ErrorKind::PermissionDenied);
121            }
122            _ => panic!("Expected IO error variant"),
123        }
124    }
125
126    #[test]
127    fn test_all_oxidize_pdf_error_variants() {
128        let errors = vec![
129            OxidizePdfError::ParseError("parse error".to_string()),
130            OxidizePdfError::InvalidStructure("structure error".to_string()),
131            OxidizePdfError::EncodingError("encoding error".to_string()),
132            OxidizePdfError::Other("other error".to_string()),
133        ];
134
135        // Test that all variants can be created and displayed
136        for error in errors {
137            let error_string = error.to_string();
138            assert!(!error_string.is_empty());
139            assert!(error_string.contains("error"));
140        }
141    }
142
143    #[test]
144    fn test_result_type_ok() {
145        let result: Result<i32> = Ok(42);
146        assert!(result.is_ok());
147        assert_eq!(result.unwrap(), 42);
148    }
149
150    #[test]
151    fn test_result_type_err() {
152        let result: Result<i32> = Err(PdfError::InvalidStructure("test".to_string()));
153        assert!(result.is_err());
154        
155        let error = result.unwrap_err();
156        match error {
157            PdfError::InvalidStructure(msg) => assert_eq!(msg, "test"),
158            _ => panic!("Expected InvalidStructure variant"),
159        }
160    }
161
162    #[test]
163    fn test_error_chain_display() {
164        // Test that error messages are properly formatted
165        let errors = [
166            ("Invalid PDF structure: corrupted header", 
167             PdfError::InvalidStructure("corrupted header".to_string())),
168            ("Invalid object reference: 999 0 R", 
169             PdfError::InvalidReference("999 0 R".to_string())),
170            ("Encoding error: unsupported encoding", 
171             PdfError::EncodingError("unsupported encoding".to_string())),
172            ("Font error: missing font", 
173             PdfError::FontError("missing font".to_string())),
174            ("Compression error: deflate failed", 
175             PdfError::CompressionError("deflate failed".to_string())),
176            ("Invalid image: corrupt JPEG", 
177             PdfError::InvalidImage("corrupt JPEG".to_string())),
178        ];
179
180        for (expected, error) in errors {
181            assert_eq!(error.to_string(), expected);
182        }
183    }
184
185    #[test]
186    fn test_oxidize_pdf_error_chain_display() {
187        // Test that OxidizePdfError messages are properly formatted
188        let errors = [
189            ("Parse error: unexpected token", 
190             OxidizePdfError::ParseError("unexpected token".to_string())),
191            ("Invalid PDF structure: missing xref", 
192             OxidizePdfError::InvalidStructure("missing xref".to_string())),
193            ("Encoding error: invalid UTF-8", 
194             OxidizePdfError::EncodingError("invalid UTF-8".to_string())),
195            ("Other error: unknown issue", 
196             OxidizePdfError::Other("unknown issue".to_string())),
197        ];
198
199        for (expected, error) in errors {
200            assert_eq!(error.to_string(), expected);
201        }
202    }
203
204    #[test]
205    fn test_error_send_sync() {
206        // Ensure error types implement Send + Sync for thread safety
207        fn assert_send_sync<T: Send + Sync>() {}
208        assert_send_sync::<PdfError>();
209        assert_send_sync::<OxidizePdfError>();
210    }
211
212    #[test]
213    fn test_error_struct_creation() {
214        // Test creating errors with string messages
215        let errors = vec![
216            PdfError::InvalidStructure("test".to_string()),
217            PdfError::InvalidReference("ref".to_string()),
218            PdfError::EncodingError("encoding".to_string()),
219            PdfError::FontError("font".to_string()),
220            PdfError::CompressionError("compression".to_string()),
221            PdfError::InvalidImage("image".to_string()),
222        ];
223        
224        // Verify each error can be created and has the expected message structure
225        for error in errors {
226            let msg = error.to_string();
227            assert!(msg.contains("error") || msg.contains("Invalid"));
228        }
229    }
230
231    #[test]
232    fn test_oxidize_pdf_error_struct_creation() {
233        // Test creating OxidizePdfError with string messages  
234        let errors = vec![
235            OxidizePdfError::ParseError("test".to_string()),
236            OxidizePdfError::InvalidStructure("structure".to_string()),
237            OxidizePdfError::EncodingError("encoding".to_string()),
238            OxidizePdfError::Other("other".to_string()),
239        ];
240        
241        // Verify each error can be created and has the expected message structure
242        for error in errors {
243            let msg = error.to_string();
244            assert!(msg.contains("error") || msg.contains("Invalid"));
245        }
246    }
247
248    #[test]
249    fn test_error_equality() {
250        let error1 = PdfError::InvalidStructure("test".to_string());
251        let error2 = PdfError::InvalidStructure("test".to_string());
252        let error3 = PdfError::InvalidStructure("different".to_string());
253        
254        // Note: thiserror doesn't automatically derive PartialEq, so we test the display output
255        assert_eq!(error1.to_string(), error2.to_string());
256        assert_ne!(error1.to_string(), error3.to_string());
257    }
258
259    #[test]
260    fn test_io_error_preservation() {
261        // Test that IO error details are preserved through conversion
262        let original_io_error = IoError::new(ErrorKind::UnexpectedEof, "sudden EOF");
263        let pdf_error = PdfError::from(original_io_error);
264        
265        if let PdfError::Io(io_err) = pdf_error {
266            assert_eq!(io_err.kind(), ErrorKind::UnexpectedEof);
267            assert_eq!(io_err.to_string(), "sudden EOF");
268        } else {
269            panic!("IO error should be preserved as PdfError::Io");
270        }
271    }
272
273    #[test]
274    fn test_oxidize_pdf_error_io_error_preservation() {
275        // Test that IO error details are preserved through conversion
276        let original_io_error = IoError::new(ErrorKind::InvalidData, "corrupted data");
277        let oxidize_error = OxidizePdfError::from(original_io_error);
278        
279        if let OxidizePdfError::Io(io_err) = oxidize_error {
280            assert_eq!(io_err.kind(), ErrorKind::InvalidData);
281            assert_eq!(io_err.to_string(), "corrupted data");
282        } else {
283            panic!("IO error should be preserved as OxidizePdfError::Io");
284        }
285    }
286}