Skip to main content

oxidize_pdf/text/invoice/
error.rs

1//! Error types for invoice extraction
2
3use thiserror::Error;
4
5/// Errors that can occur during invoice extraction
6#[derive(Debug, Error)]
7pub enum ExtractionError {
8    /// No text found on the specified page
9    #[error("No text found on page {0}")]
10    NoTextFound(u32),
11
12    /// Pattern matching failed with details
13    #[error("Pattern matching failed: {0}")]
14    PatternError(String),
15
16    /// Invalid configuration provided
17    #[error("Invalid configuration: {0}")]
18    ConfigError(String),
19
20    /// Unsupported language specified
21    #[error("Unsupported language: {0}")]
22    UnsupportedLanguage(String),
23
24    /// Invalid confidence threshold (must be 0.0-1.0)
25    #[error("Invalid confidence threshold: {0} (must be between 0.0 and 1.0)")]
26    InvalidThreshold(f64),
27
28    /// Regex compilation error
29    #[error("Regex compilation error: {0}")]
30    RegexError(String),
31
32    /// Generic extraction error with context
33    #[error("Extraction error: {0}")]
34    Generic(String),
35}
36
37/// Result type for invoice extraction operations
38pub type Result<T> = std::result::Result<T, ExtractionError>;
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_no_text_found_error() {
46        let err = ExtractionError::NoTextFound(5);
47        assert_eq!(format!("{}", err), "No text found on page 5");
48    }
49
50    #[test]
51    fn test_no_text_found_page_zero() {
52        let err = ExtractionError::NoTextFound(0);
53        assert_eq!(format!("{}", err), "No text found on page 0");
54    }
55
56    #[test]
57    fn test_pattern_error() {
58        let err = ExtractionError::PatternError("Invalid regex".to_string());
59        assert_eq!(format!("{}", err), "Pattern matching failed: Invalid regex");
60    }
61
62    #[test]
63    fn test_config_error() {
64        let err = ExtractionError::ConfigError("Missing required field".to_string());
65        assert_eq!(
66            format!("{}", err),
67            "Invalid configuration: Missing required field"
68        );
69    }
70
71    #[test]
72    fn test_unsupported_language() {
73        let err = ExtractionError::UnsupportedLanguage("Klingon".to_string());
74        assert_eq!(format!("{}", err), "Unsupported language: Klingon");
75    }
76
77    #[test]
78    fn test_invalid_threshold_negative() {
79        let err = ExtractionError::InvalidThreshold(-0.5);
80        assert_eq!(
81            format!("{}", err),
82            "Invalid confidence threshold: -0.5 (must be between 0.0 and 1.0)"
83        );
84    }
85
86    #[test]
87    fn test_invalid_threshold_too_high() {
88        let err = ExtractionError::InvalidThreshold(1.5);
89        assert_eq!(
90            format!("{}", err),
91            "Invalid confidence threshold: 1.5 (must be between 0.0 and 1.0)"
92        );
93    }
94
95    #[test]
96    fn test_regex_error() {
97        let err = ExtractionError::RegexError("Unmatched parenthesis".to_string());
98        assert_eq!(
99            format!("{}", err),
100            "Regex compilation error: Unmatched parenthesis"
101        );
102    }
103
104    #[test]
105    fn test_generic_error() {
106        let err = ExtractionError::Generic("Something went wrong".to_string());
107        assert_eq!(format!("{}", err), "Extraction error: Something went wrong");
108    }
109
110    #[test]
111    fn test_error_debug_impl() {
112        let err = ExtractionError::NoTextFound(3);
113        // Debug trait should be implemented
114        let debug_str = format!("{:?}", err);
115        assert!(debug_str.contains("NoTextFound"));
116        assert!(debug_str.contains("3"));
117    }
118
119    #[test]
120    fn test_error_is_send_sync() {
121        // Verify ExtractionError implements Send + Sync for thread safety
122        fn assert_send_sync<T: Send + Sync>() {}
123        assert_send_sync::<ExtractionError>();
124    }
125
126    #[test]
127    fn test_result_type_alias() {
128        // Test that Result type alias works correctly
129        fn returns_ok() -> Result<i32> {
130            Ok(42)
131        }
132
133        fn returns_err() -> Result<i32> {
134            Err(ExtractionError::Generic("test".to_string()))
135        }
136
137        assert!(returns_ok().is_ok());
138        assert!(returns_err().is_err());
139    }
140}