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 #[error("Invalid operation: {0}")]
54 InvalidOperation(String),
55
56 #[error("Duplicate field: {0}")]
57 DuplicateField(String),
58
59 #[error("Field not found: {0}")]
60 FieldNotFound(String),
61
62 #[error("External validation error: {0}")]
63 ExternalValidationError(String),
64
65 #[error("Internal error: {0}")]
66 Internal(String),
67
68 #[error("Serialization error: {0}")]
69 SerializationError(String),
70}
71
72pub type Result<T> = std::result::Result<T, PdfError>;
73
74impl From<crate::encryption::AesError> for PdfError {
76 fn from(err: crate::encryption::AesError) -> Self {
77 PdfError::EncryptionError(err.to_string())
78 }
79}
80
81impl From<crate::parser::ParseError> for PdfError {
82 fn from(err: crate::parser::ParseError) -> Self {
83 PdfError::ParseError(err.to_string())
84 }
85}
86
87#[derive(Error, Debug)]
89pub enum OxidizePdfError {
90 #[error("IO error: {0}")]
91 Io(#[from] std::io::Error),
92
93 #[error("Parse error: {0}")]
94 ParseError(String),
95
96 #[error("Invalid PDF structure: {0}")]
97 InvalidStructure(String),
98
99 #[error("Encoding error: {0}")]
100 EncodingError(String),
101
102 #[error("Other error: {0}")]
103 Other(String),
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use std::io::{Error as IoError, ErrorKind};
110
111 #[test]
112 fn test_pdf_error_display() {
113 let error = PdfError::InvalidStructure("test message".to_string());
114 assert_eq!(error.to_string(), "Invalid PDF structure: test message");
115 }
116
117 #[test]
118 fn test_pdf_error_debug() {
119 let error = PdfError::InvalidReference("object 1 0".to_string());
120 let debug_str = format!("{error:?}");
121 assert!(debug_str.contains("InvalidReference"));
122 assert!(debug_str.contains("object 1 0"));
123 }
124
125 #[test]
126 fn test_pdf_error_from_io_error() {
127 let io_error = IoError::new(ErrorKind::NotFound, "file not found");
128 let pdf_error = PdfError::from(io_error);
129
130 match pdf_error {
131 PdfError::Io(ref err) => {
132 assert_eq!(err.kind(), ErrorKind::NotFound);
133 }
134 _ => panic!("Expected IO error variant"),
135 }
136 }
137
138 #[test]
139 fn test_all_pdf_error_variants() {
140 let errors = vec![
141 PdfError::InvalidStructure("structure error".to_string()),
142 PdfError::InvalidObjectReference(1, 0),
143 PdfError::EncodingError("encoding error".to_string()),
144 PdfError::FontError("font error".to_string()),
145 PdfError::CompressionError("compression error".to_string()),
146 PdfError::InvalidImage("image error".to_string()),
147 PdfError::ParseError("parse error".to_string()),
148 PdfError::InvalidPageNumber(999),
149 PdfError::InvalidFormat("format error".to_string()),
150 PdfError::InvalidHeader,
151 PdfError::ContentStreamTooLarge(1024 * 1024),
152 ];
153
154 for error in errors {
156 let error_string = error.to_string();
157 assert!(!error_string.is_empty());
158 }
159 }
160
161 #[test]
162 fn test_oxidize_pdf_error_display() {
163 let error = OxidizePdfError::ParseError("parsing failed".to_string());
164 assert_eq!(error.to_string(), "Parse error: parsing failed");
165 }
166
167 #[test]
168 fn test_oxidize_pdf_error_debug() {
169 let error = OxidizePdfError::InvalidStructure("malformed PDF".to_string());
170 let debug_str = format!("{error:?}");
171 assert!(debug_str.contains("InvalidStructure"));
172 assert!(debug_str.contains("malformed PDF"));
173 }
174
175 #[test]
176 fn test_oxidize_pdf_error_from_io_error() {
177 let io_error = IoError::new(ErrorKind::PermissionDenied, "access denied");
178 let pdf_error = OxidizePdfError::from(io_error);
179
180 match pdf_error {
181 OxidizePdfError::Io(ref err) => {
182 assert_eq!(err.kind(), ErrorKind::PermissionDenied);
183 }
184 _ => panic!("Expected IO error variant"),
185 }
186 }
187
188 #[test]
189 fn test_all_oxidize_pdf_error_variants() {
190 let errors = vec![
191 OxidizePdfError::ParseError("parse error".to_string()),
192 OxidizePdfError::InvalidStructure("structure error".to_string()),
193 OxidizePdfError::EncodingError("encoding error".to_string()),
194 OxidizePdfError::Other("other error".to_string()),
195 ];
196
197 for error in errors {
199 let error_string = error.to_string();
200 assert!(!error_string.is_empty());
201 assert!(error_string.contains("error"));
202 }
203 }
204
205 #[test]
206 fn test_result_type_ok() {
207 let result: Result<i32> = Ok(42);
208 assert!(result.is_ok());
209 assert_eq!(result.unwrap(), 42);
210 }
211
212 #[test]
213 fn test_result_type_err() {
214 let result: Result<i32> = Err(PdfError::InvalidStructure("test".to_string()));
215 assert!(result.is_err());
216
217 let error = result.unwrap_err();
218 match error {
219 PdfError::InvalidStructure(msg) => assert_eq!(msg, "test"),
220 _ => panic!("Expected InvalidStructure variant"),
221 }
222 }
223
224 #[test]
225 fn test_error_chain_display() {
226 let errors = [
228 (
229 "Invalid PDF structure: corrupted header",
230 PdfError::InvalidStructure("corrupted header".to_string()),
231 ),
232 (
233 "Invalid object reference: 999 0 R",
234 PdfError::InvalidObjectReference(999, 0),
235 ),
236 (
237 "Encoding error: unsupported encoding",
238 PdfError::EncodingError("unsupported encoding".to_string()),
239 ),
240 (
241 "Font error: missing font",
242 PdfError::FontError("missing font".to_string()),
243 ),
244 (
245 "Compression error: deflate failed",
246 PdfError::CompressionError("deflate failed".to_string()),
247 ),
248 (
249 "Invalid image: corrupt JPEG",
250 PdfError::InvalidImage("corrupt JPEG".to_string()),
251 ),
252 ];
253
254 for (expected, error) in errors {
255 assert_eq!(error.to_string(), expected);
256 }
257 }
258
259 #[test]
260 fn test_oxidize_pdf_error_chain_display() {
261 let errors = [
263 (
264 "Parse error: unexpected token",
265 OxidizePdfError::ParseError("unexpected token".to_string()),
266 ),
267 (
268 "Invalid PDF structure: missing xref",
269 OxidizePdfError::InvalidStructure("missing xref".to_string()),
270 ),
271 (
272 "Encoding error: invalid UTF-8",
273 OxidizePdfError::EncodingError("invalid UTF-8".to_string()),
274 ),
275 (
276 "Other error: unknown issue",
277 OxidizePdfError::Other("unknown issue".to_string()),
278 ),
279 ];
280
281 for (expected, error) in errors {
282 assert_eq!(error.to_string(), expected);
283 }
284 }
285
286 #[test]
287 fn test_error_send_sync() {
288 fn assert_send_sync<T: Send + Sync>() {}
290 assert_send_sync::<PdfError>();
291 assert_send_sync::<OxidizePdfError>();
292 }
293
294 #[test]
295 fn test_error_struct_creation() {
296 let errors = vec![
298 PdfError::InvalidStructure("test".to_string()),
299 PdfError::InvalidObjectReference(1, 0),
300 PdfError::EncodingError("encoding".to_string()),
301 PdfError::FontError("font".to_string()),
302 PdfError::CompressionError("compression".to_string()),
303 PdfError::InvalidImage("image".to_string()),
304 PdfError::ParseError("parse".to_string()),
305 PdfError::InvalidPageNumber(1),
306 PdfError::InvalidFormat("format".to_string()),
307 PdfError::InvalidHeader,
308 PdfError::ContentStreamTooLarge(1024),
309 PdfError::OperationCancelled,
310 ];
311
312 for error in errors {
314 let msg = error.to_string();
315 assert!(!msg.is_empty(), "Error message should not be empty");
316
317 match &error {
319 PdfError::OperationCancelled => assert!(msg.contains("cancelled")),
320 PdfError::ContentStreamTooLarge(_) => assert!(msg.contains("too large")),
321 _ => assert!(msg.contains("error") || msg.contains("Invalid")),
322 }
323 }
324 }
325
326 #[test]
327 fn test_oxidize_pdf_error_struct_creation() {
328 let errors = vec![
330 OxidizePdfError::ParseError("test".to_string()),
331 OxidizePdfError::InvalidStructure("structure".to_string()),
332 OxidizePdfError::EncodingError("encoding".to_string()),
333 OxidizePdfError::Other("other".to_string()),
334 ];
335
336 for error in errors {
338 let msg = error.to_string();
339 assert!(msg.contains("error") || msg.contains("Invalid"));
340 }
341 }
342
343 #[test]
344 fn test_error_equality() {
345 let error1 = PdfError::InvalidStructure("test".to_string());
346 let error2 = PdfError::InvalidStructure("test".to_string());
347 let error3 = PdfError::InvalidStructure("different".to_string());
348
349 assert_eq!(error1.to_string(), error2.to_string());
351 assert_ne!(error1.to_string(), error3.to_string());
352 }
353
354 #[test]
355 fn test_io_error_preservation() {
356 let original_io_error = IoError::new(ErrorKind::UnexpectedEof, "sudden EOF");
358 let pdf_error = PdfError::from(original_io_error);
359
360 if let PdfError::Io(io_err) = pdf_error {
361 assert_eq!(io_err.kind(), ErrorKind::UnexpectedEof);
362 assert_eq!(io_err.to_string(), "sudden EOF");
363 } else {
364 panic!("IO error should be preserved as PdfError::Io");
365 }
366 }
367
368 #[test]
369 fn test_oxidize_pdf_error_io_error_preservation() {
370 let original_io_error = IoError::new(ErrorKind::InvalidData, "corrupted data");
372 let oxidize_error = OxidizePdfError::from(original_io_error);
373
374 if let OxidizePdfError::Io(io_err) = oxidize_error {
375 assert_eq!(io_err.kind(), ErrorKind::InvalidData);
376 assert_eq!(io_err.to_string(), "corrupted data");
377 } else {
378 panic!("IO error should be preserved as OxidizePdfError::Io");
379 }
380 }
381
382 #[test]
383 fn test_operation_cancelled_error() {
384 let error = PdfError::OperationCancelled;
386 assert_eq!(error.to_string(), "Operation cancelled");
387
388 let result: Result<()> = Err(PdfError::OperationCancelled);
390 assert!(result.is_err());
391 if let Err(PdfError::OperationCancelled) = result {
392 } else {
394 panic!("Expected OperationCancelled variant");
395 }
396 }
397
398 #[test]
399 fn test_encryption_error() {
400 let error = PdfError::EncryptionError("AES decryption failed".to_string());
402 assert_eq!(error.to_string(), "Encryption error: AES decryption failed");
403
404 let debug_str = format!("{:?}", error);
406 assert!(debug_str.contains("EncryptionError"));
407 assert!(debug_str.contains("AES decryption failed"));
408 }
409
410 #[test]
411 fn test_permission_denied_error() {
412 let error = PdfError::PermissionDenied("Cannot modify protected document".to_string());
414 assert_eq!(
415 error.to_string(),
416 "Permission denied: Cannot modify protected document"
417 );
418
419 let other_error = PdfError::InvalidOperation("Cannot modify".to_string());
421 assert_ne!(error.to_string(), other_error.to_string());
422 }
423
424 #[test]
425 fn test_invalid_operation_error() {
426 let error =
428 PdfError::InvalidOperation("Cannot perform operation on encrypted PDF".to_string());
429 assert_eq!(
430 error.to_string(),
431 "Invalid operation: Cannot perform operation on encrypted PDF"
432 );
433
434 match error {
436 PdfError::InvalidOperation(msg) => {
437 assert!(msg.contains("encrypted"));
438 }
439 _ => panic!("Expected InvalidOperation variant"),
440 }
441 }
442
443 #[test]
444 fn test_duplicate_field_error() {
445 let field_name = "email_address";
447 let error = PdfError::DuplicateField(field_name.to_string());
448 assert_eq!(error.to_string(), "Duplicate field: email_address");
449
450 let empty_error = PdfError::DuplicateField(String::new());
452 assert_eq!(empty_error.to_string(), "Duplicate field: ");
453 }
454
455 #[test]
456 fn test_field_not_found_error() {
457 let field_name = "signature_field";
459 let error = PdfError::FieldNotFound(field_name.to_string());
460 assert_eq!(error.to_string(), "Field not found: signature_field");
461
462 let special_field = "field[0].subfield";
464 let special_error = PdfError::FieldNotFound(special_field.to_string());
465 assert_eq!(
466 special_error.to_string(),
467 "Field not found: field[0].subfield"
468 );
469 }
470
471 #[test]
472 fn test_aes_error_conversion() {
473 use crate::encryption::AesError;
476
477 let aes_error = AesError::InvalidKeyLength {
478 expected: 32,
479 actual: 16,
480 };
481 let pdf_error: PdfError = aes_error.into();
482
483 match pdf_error {
484 PdfError::EncryptionError(msg) => {
485 assert!(msg.contains("Invalid key length") || msg.contains("InvalidKeyLength"));
486 }
487 _ => panic!("Expected EncryptionError from AesError conversion"),
488 }
489 }
490
491 #[test]
492 fn test_parse_error_conversion() {
493 use crate::parser::ParseError;
495
496 let parse_error = ParseError::InvalidXRef;
497 let pdf_error: PdfError = parse_error.into();
498
499 match pdf_error {
500 PdfError::ParseError(msg) => {
501 assert!(msg.contains("XRef") || msg.contains("Invalid"));
502 }
503 _ => panic!("Expected ParseError from ParseError conversion"),
504 }
505 }
506}