use std::io;
use std::path::PathBuf;
use thiserror::Error;
pub type PdfResult<T> = Result<T, PdfError>;
#[derive(Error, Debug)]
pub enum PdfError {
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("PDF file not found: {0}")]
FileNotFound(PathBuf),
#[error("Invalid PDF file: {0}")]
InvalidPdf(String),
#[error("Backend '{backend}' not available: {reason}")]
BackendNotAvailable {
backend: String,
reason: String,
},
#[error("Backend '{backend}' failed: {message}")]
BackendFailed {
backend: String,
message: String,
},
#[error("Backend '{backend}' returned invalid output: {reason}")]
InvalidOutput {
backend: String,
reason: String,
},
#[error("Extraction timed out after {seconds}s")]
Timeout {
seconds: u64,
},
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Postprocessing failed: {0}")]
Postprocess(String),
#[error("LaTeX validation failed: {0}")]
Validation(String),
#[error("Python environment error: {0}")]
PythonEnvironment(String),
#[error("Resource exhaustion: {0}")]
ResourceExhaustion(String),
#[error("Failed to extract pages: {pages:?}")]
PageExtractionFailed {
pages: Vec<usize>,
},
#[error("Unsupported PDF feature: {0}")]
UnsupportedFeature(String),
}
impl PdfError {
pub fn is_recoverable(&self) -> bool {
matches!(
self,
PdfError::PageExtractionFailed { .. } | PdfError::Timeout { .. }
)
}
pub fn is_backend_missing(&self) -> bool {
matches!(self, PdfError::BackendNotAvailable { .. })
}
pub fn backend_name(&self) -> Option<&str> {
match self {
PdfError::BackendNotAvailable { backend, .. } => Some(backend),
PdfError::BackendFailed { backend, .. } => Some(backend),
PdfError::InvalidOutput { backend, .. } => Some(backend),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_recoverable() {
let err = PdfError::PageExtractionFailed { pages: vec![1, 2] };
assert!(err.is_recoverable());
let err = PdfError::InvalidPdf("corrupt".into());
assert!(!err.is_recoverable());
}
#[test]
fn test_error_backend_name() {
let err = PdfError::BackendNotAvailable {
backend: "marker".into(),
reason: "not found".into(),
};
assert_eq!(err.backend_name(), Some("marker"));
let err = PdfError::Io(io::Error::new(io::ErrorKind::NotFound, "test"));
assert_eq!(err.backend_name(), None);
}
}