Skip to main content

pdfplumber_parse/
error.rs

1//! Error types for the parsing and interpreter layers.
2//!
3//! Uses [`thiserror`] for ergonomic error derivation. Provides [`BackendError`]
4//! that wraps backend-specific errors and converts them to [`PdfError`].
5
6use pdfplumber_core::PdfError;
7use thiserror::Error;
8
9/// Error type for PDF parsing backend operations.
10///
11/// Wraps backend-specific errors and provides conversion to [`PdfError`]
12/// for unified error handling across the library.
13#[derive(Debug, Error)]
14pub enum BackendError {
15    /// Error from PDF parsing (structure, syntax, object resolution).
16    #[error("PDF parse error: {0}")]
17    Parse(String),
18
19    /// Error reading PDF data.
20    #[error("I/O error: {0}")]
21    Io(#[from] std::io::Error),
22
23    /// Error resolving font or encoding information.
24    #[error("font error: {0}")]
25    Font(String),
26
27    /// Error during content stream interpretation.
28    #[error("interpreter error: {0}")]
29    Interpreter(String),
30
31    /// A core library error.
32    #[error(transparent)]
33    Core(#[from] PdfError),
34}
35
36impl From<BackendError> for PdfError {
37    fn from(err: BackendError) -> Self {
38        match err {
39            BackendError::Parse(msg) => PdfError::ParseError(msg),
40            BackendError::Io(e) => PdfError::IoError(e.to_string()),
41            BackendError::Font(msg) => PdfError::FontError(msg),
42            BackendError::Interpreter(msg) => PdfError::InterpreterError(msg),
43            BackendError::Core(e) => e,
44        }
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn backend_error_parse() {
54        let err = BackendError::Parse("invalid xref table".to_string());
55        assert_eq!(err.to_string(), "PDF parse error: invalid xref table");
56    }
57
58    #[test]
59    fn backend_error_io_from_std() {
60        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
61        let err: BackendError = io_err.into();
62        assert!(matches!(err, BackendError::Io(_)));
63        assert!(err.to_string().contains("file missing"));
64    }
65
66    #[test]
67    fn backend_error_from_pdf_error() {
68        let pdf_err = PdfError::FontError("bad metrics".to_string());
69        let err: BackendError = pdf_err.into();
70        assert!(matches!(err, BackendError::Core(_)));
71    }
72
73    #[test]
74    fn backend_error_to_pdf_error_parse() {
75        let backend = BackendError::Parse("bad syntax".to_string());
76        let pdf_err: PdfError = backend.into();
77        assert_eq!(pdf_err, PdfError::ParseError("bad syntax".to_string()));
78    }
79
80    #[test]
81    fn backend_error_to_pdf_error_io() {
82        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
83        let backend = BackendError::Io(io_err);
84        let pdf_err: PdfError = backend.into();
85        assert!(matches!(pdf_err, PdfError::IoError(_)));
86        assert!(pdf_err.to_string().contains("denied"));
87    }
88
89    #[test]
90    fn backend_error_to_pdf_error_font() {
91        let backend = BackendError::Font("missing widths".to_string());
92        let pdf_err: PdfError = backend.into();
93        assert_eq!(pdf_err, PdfError::FontError("missing widths".to_string()));
94    }
95
96    #[test]
97    fn backend_error_to_pdf_error_interpreter() {
98        let backend = BackendError::Interpreter("stack underflow".to_string());
99        let pdf_err: PdfError = backend.into();
100        assert_eq!(
101            pdf_err,
102            PdfError::InterpreterError("stack underflow".to_string())
103        );
104    }
105
106    #[test]
107    fn backend_error_to_pdf_error_core_passthrough() {
108        let original = PdfError::ResourceLimitExceeded("too large".to_string());
109        let backend = BackendError::Core(original.clone());
110        let pdf_err: PdfError = backend.into();
111        assert_eq!(pdf_err, original);
112    }
113
114    #[test]
115    fn backend_error_implements_std_error() {
116        let err: Box<dyn std::error::Error> = Box::new(BackendError::Parse("test".to_string()));
117        assert!(err.to_string().contains("test"));
118    }
119}