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