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