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 #[error("Invalid object reference: {0} {1} R")]
27 InvalidObjectReference(u32, u16),
28
29 #[error("Parse error: {0}")]
30 ParseError(String),
31
32 #[error("Invalid page number: {0}")]
33 InvalidPageNumber(u32),
34
35 #[error("Invalid format: {0}")]
36 InvalidFormat(String),
37
38 #[error("Invalid header")]
39 InvalidHeader,
40
41 #[error("Content stream too large: {0} bytes")]
42 ContentStreamTooLarge(usize),
43
44 #[error("Operation cancelled")]
45 OperationCancelled,
46
47 #[error("Encryption error: {0}")]
48 EncryptionError(String),
49
50 #[error("Permission denied: {0}")]
51 PermissionDenied(String),
52}
53
54pub type Result<T> = std::result::Result<T, PdfError>;
55
56impl From<crate::encryption::AesError> for PdfError {
58 fn from(err: crate::encryption::AesError) -> Self {
59 PdfError::EncryptionError(err.to_string())
60 }
61}
62
63impl From<crate::parser::ParseError> for PdfError {
64 fn from(err: crate::parser::ParseError) -> Self {
65 PdfError::ParseError(err.to_string())
66 }
67}
68
69#[derive(Error, Debug)]
71pub enum OxidizePdfError {
72 #[error("IO error: {0}")]
73 Io(#[from] std::io::Error),
74
75 #[error("Parse error: {0}")]
76 ParseError(String),
77
78 #[error("Invalid PDF structure: {0}")]
79 InvalidStructure(String),
80
81 #[error("Encoding error: {0}")]
82 EncodingError(String),
83
84 #[error("Other error: {0}")]
85 Other(String),
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use std::io::{Error as IoError, ErrorKind};
92
93 #[test]
94 fn test_pdf_error_display() {
95 let error = PdfError::InvalidStructure("test message".to_string());
96 assert_eq!(error.to_string(), "Invalid PDF structure: test message");
97 }
98
99 #[test]
100 fn test_pdf_error_debug() {
101 let error = PdfError::InvalidReference("object 1 0".to_string());
102 let debug_str = format!("{:?}", error);
103 assert!(debug_str.contains("InvalidReference"));
104 assert!(debug_str.contains("object 1 0"));
105 }
106
107 #[test]
108 fn test_pdf_error_from_io_error() {
109 let io_error = IoError::new(ErrorKind::NotFound, "file not found");
110 let pdf_error = PdfError::from(io_error);
111
112 match pdf_error {
113 PdfError::Io(ref err) => {
114 assert_eq!(err.kind(), ErrorKind::NotFound);
115 }
116 _ => panic!("Expected IO error variant"),
117 }
118 }
119
120 #[test]
121 fn test_all_pdf_error_variants() {
122 let errors = vec![
123 PdfError::InvalidStructure("structure error".to_string()),
124 PdfError::InvalidObjectReference(1, 0),
125 PdfError::EncodingError("encoding error".to_string()),
126 PdfError::FontError("font error".to_string()),
127 PdfError::CompressionError("compression error".to_string()),
128 PdfError::InvalidImage("image error".to_string()),
129 PdfError::ParseError("parse error".to_string()),
130 PdfError::InvalidPageNumber(999),
131 PdfError::InvalidFormat("format error".to_string()),
132 PdfError::InvalidHeader,
133 PdfError::ContentStreamTooLarge(1024 * 1024),
134 ];
135
136 for error in errors {
138 let error_string = error.to_string();
139 assert!(!error_string.is_empty());
140 }
141 }
142
143 #[test]
144 fn test_oxidize_pdf_error_display() {
145 let error = OxidizePdfError::ParseError("parsing failed".to_string());
146 assert_eq!(error.to_string(), "Parse error: parsing failed");
147 }
148
149 #[test]
150 fn test_oxidize_pdf_error_debug() {
151 let error = OxidizePdfError::InvalidStructure("malformed PDF".to_string());
152 let debug_str = format!("{:?}", error);
153 assert!(debug_str.contains("InvalidStructure"));
154 assert!(debug_str.contains("malformed PDF"));
155 }
156
157 #[test]
158 fn test_oxidize_pdf_error_from_io_error() {
159 let io_error = IoError::new(ErrorKind::PermissionDenied, "access denied");
160 let pdf_error = OxidizePdfError::from(io_error);
161
162 match pdf_error {
163 OxidizePdfError::Io(ref err) => {
164 assert_eq!(err.kind(), ErrorKind::PermissionDenied);
165 }
166 _ => panic!("Expected IO error variant"),
167 }
168 }
169
170 #[test]
171 fn test_all_oxidize_pdf_error_variants() {
172 let errors = vec![
173 OxidizePdfError::ParseError("parse error".to_string()),
174 OxidizePdfError::InvalidStructure("structure error".to_string()),
175 OxidizePdfError::EncodingError("encoding error".to_string()),
176 OxidizePdfError::Other("other error".to_string()),
177 ];
178
179 for error in errors {
181 let error_string = error.to_string();
182 assert!(!error_string.is_empty());
183 assert!(error_string.contains("error"));
184 }
185 }
186
187 #[test]
188 fn test_result_type_ok() {
189 let result: Result<i32> = Ok(42);
190 assert!(result.is_ok());
191 assert_eq!(result.unwrap(), 42);
192 }
193
194 #[test]
195 fn test_result_type_err() {
196 let result: Result<i32> = Err(PdfError::InvalidStructure("test".to_string()));
197 assert!(result.is_err());
198
199 let error = result.unwrap_err();
200 match error {
201 PdfError::InvalidStructure(msg) => assert_eq!(msg, "test"),
202 _ => panic!("Expected InvalidStructure variant"),
203 }
204 }
205
206 #[test]
207 fn test_error_chain_display() {
208 let errors = [
210 (
211 "Invalid PDF structure: corrupted header",
212 PdfError::InvalidStructure("corrupted header".to_string()),
213 ),
214 (
215 "Invalid object reference: 999 0 R",
216 PdfError::InvalidObjectReference(999, 0),
217 ),
218 (
219 "Encoding error: unsupported encoding",
220 PdfError::EncodingError("unsupported encoding".to_string()),
221 ),
222 (
223 "Font error: missing font",
224 PdfError::FontError("missing font".to_string()),
225 ),
226 (
227 "Compression error: deflate failed",
228 PdfError::CompressionError("deflate failed".to_string()),
229 ),
230 (
231 "Invalid image: corrupt JPEG",
232 PdfError::InvalidImage("corrupt JPEG".to_string()),
233 ),
234 ];
235
236 for (expected, error) in errors {
237 assert_eq!(error.to_string(), expected);
238 }
239 }
240
241 #[test]
242 fn test_oxidize_pdf_error_chain_display() {
243 let errors = [
245 (
246 "Parse error: unexpected token",
247 OxidizePdfError::ParseError("unexpected token".to_string()),
248 ),
249 (
250 "Invalid PDF structure: missing xref",
251 OxidizePdfError::InvalidStructure("missing xref".to_string()),
252 ),
253 (
254 "Encoding error: invalid UTF-8",
255 OxidizePdfError::EncodingError("invalid UTF-8".to_string()),
256 ),
257 (
258 "Other error: unknown issue",
259 OxidizePdfError::Other("unknown issue".to_string()),
260 ),
261 ];
262
263 for (expected, error) in errors {
264 assert_eq!(error.to_string(), expected);
265 }
266 }
267
268 #[test]
269 fn test_error_send_sync() {
270 fn assert_send_sync<T: Send + Sync>() {}
272 assert_send_sync::<PdfError>();
273 assert_send_sync::<OxidizePdfError>();
274 }
275
276 #[test]
277 fn test_error_struct_creation() {
278 let errors = vec![
280 PdfError::InvalidStructure("test".to_string()),
281 PdfError::InvalidObjectReference(1, 0),
282 PdfError::EncodingError("encoding".to_string()),
283 PdfError::FontError("font".to_string()),
284 PdfError::CompressionError("compression".to_string()),
285 PdfError::InvalidImage("image".to_string()),
286 PdfError::ParseError("parse".to_string()),
287 PdfError::InvalidPageNumber(1),
288 PdfError::InvalidFormat("format".to_string()),
289 PdfError::InvalidHeader,
290 PdfError::ContentStreamTooLarge(1024),
291 PdfError::OperationCancelled,
292 ];
293
294 for error in errors {
296 let msg = error.to_string();
297 assert!(!msg.is_empty(), "Error message should not be empty");
298
299 match &error {
301 PdfError::OperationCancelled => assert!(msg.contains("cancelled")),
302 PdfError::ContentStreamTooLarge(_) => assert!(msg.contains("too large")),
303 _ => assert!(msg.contains("error") || msg.contains("Invalid")),
304 }
305 }
306 }
307
308 #[test]
309 fn test_oxidize_pdf_error_struct_creation() {
310 let errors = vec![
312 OxidizePdfError::ParseError("test".to_string()),
313 OxidizePdfError::InvalidStructure("structure".to_string()),
314 OxidizePdfError::EncodingError("encoding".to_string()),
315 OxidizePdfError::Other("other".to_string()),
316 ];
317
318 for error in errors {
320 let msg = error.to_string();
321 assert!(msg.contains("error") || msg.contains("Invalid"));
322 }
323 }
324
325 #[test]
326 fn test_error_equality() {
327 let error1 = PdfError::InvalidStructure("test".to_string());
328 let error2 = PdfError::InvalidStructure("test".to_string());
329 let error3 = PdfError::InvalidStructure("different".to_string());
330
331 assert_eq!(error1.to_string(), error2.to_string());
333 assert_ne!(error1.to_string(), error3.to_string());
334 }
335
336 #[test]
337 fn test_io_error_preservation() {
338 let original_io_error = IoError::new(ErrorKind::UnexpectedEof, "sudden EOF");
340 let pdf_error = PdfError::from(original_io_error);
341
342 if let PdfError::Io(io_err) = pdf_error {
343 assert_eq!(io_err.kind(), ErrorKind::UnexpectedEof);
344 assert_eq!(io_err.to_string(), "sudden EOF");
345 } else {
346 panic!("IO error should be preserved as PdfError::Io");
347 }
348 }
349
350 #[test]
351 fn test_oxidize_pdf_error_io_error_preservation() {
352 let original_io_error = IoError::new(ErrorKind::InvalidData, "corrupted data");
354 let oxidize_error = OxidizePdfError::from(original_io_error);
355
356 if let OxidizePdfError::Io(io_err) = oxidize_error {
357 assert_eq!(io_err.kind(), ErrorKind::InvalidData);
358 assert_eq!(io_err.to_string(), "corrupted data");
359 } else {
360 panic!("IO error should be preserved as OxidizePdfError::Io");
361 }
362 }
363}