pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[allow(clippy::enum_variant_names)] pub enum Error {
#[error("Invalid PDF header: expected '%PDF-', found '{0}'")]
InvalidHeader(String),
#[error("Unsupported PDF version: {0}")]
UnsupportedVersion(String),
#[error("Failed to parse object at byte {offset}: {reason}")]
ParseError {
offset: usize,
reason: String,
},
#[error("Parse warning at byte {offset}: {message}")]
ParseWarning {
offset: usize,
message: String,
},
#[error("Invalid cross-reference table")]
InvalidXref,
#[error("Object not found: {0} {1} R")]
ObjectNotFound(u32, u16),
#[error("Invalid object type: expected {expected}, found {found}")]
InvalidObjectType {
expected: String,
found: String,
},
#[error("End of file reached unexpectedly")]
UnexpectedEof,
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("UTF-8 decoding error: {0}")]
Utf8Error(#[from] std::str::Utf8Error),
#[error("Unsupported feature: {0}")]
Unsupported(String),
#[error("Invalid PDF: {0}")]
InvalidPdf(String),
#[error("Stream decoding error: {0}")]
Decode(String),
#[error("Encoding error: {0}")]
Encode(String),
#[error("Unsupported filter: {0}")]
UnsupportedFilter(String),
#[error("Font error: {0}")]
Font(String),
#[error("Image error: {0}")]
Image(String),
#[cfg(feature = "ml")]
#[error("ML error: {0}")]
Ml(String),
#[cfg(feature = "ocr")]
#[error("OCR error: {0}")]
Ocr(String),
#[error("Circular reference detected: object {0}")]
CircularReference(crate::object::ObjectRef),
#[error("Recursion depth limit exceeded (max: {0})")]
RecursionLimitExceeded(u32),
#[error("Invalid operation: {0}")]
InvalidOperation(String),
#[error("Layout analysis error: {0}")]
LayoutAnalysis(String),
#[error("Barcode error: {0}")]
Barcode(String),
#[error("PDF is encrypted and requires a password. Call authenticate(password) before extracting content.")]
EncryptedPdf,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_header_error() {
let err = Error::InvalidHeader("NotAPDF".to_string());
let msg = format!("{}", err);
assert!(msg.contains("Invalid PDF header"));
assert!(msg.contains("NotAPDF"));
}
#[test]
fn test_unsupported_version_error() {
let err = Error::UnsupportedVersion("3.0".to_string());
let msg = format!("{}", err);
assert!(msg.contains("Unsupported PDF version"));
assert!(msg.contains("3.0"));
}
#[test]
fn test_parse_error() {
let err = Error::ParseError {
offset: 1234,
reason: "invalid token".to_string(),
};
let msg = format!("{}", err);
assert!(msg.contains("1234"));
assert!(msg.contains("invalid token"));
}
#[test]
fn test_object_not_found_error() {
let err = Error::ObjectNotFound(10, 0);
let msg = format!("{}", err);
assert!(msg.contains("10 0 R"));
}
#[test]
fn test_invalid_object_type_error() {
let err = Error::InvalidObjectType {
expected: "Dictionary".to_string(),
found: "Array".to_string(),
};
let msg = format!("{}", err);
assert!(msg.contains("Dictionary"));
assert!(msg.contains("Array"));
}
#[test]
fn test_error_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Error>();
}
#[test]
fn test_encrypted_pdf_error() {
let err = Error::EncryptedPdf;
let msg = format!("{}", err);
assert!(msg.contains("encrypted"));
assert!(msg.contains("password"));
}
}