webp_screenshot_rust/
error.rs

1//! Error types for the webp-screenshot library
2
3use thiserror::Error;
4
5/// Main error type for screenshot capture operations
6#[derive(Error, Debug)]
7pub enum CaptureError {
8    /// Display not found or invalid index
9    #[error("Display not found: index {0}")]
10    DisplayNotFound(usize),
11
12    /// Display enumeration failed
13    #[error("Failed to enumerate displays: {0}")]
14    DisplayEnumerationFailed(String),
15
16    /// Capture operation failed
17    #[error("Capture failed: {0}")]
18    CaptureFailed(String),
19
20    /// Permission denied for screen capture
21    #[error("Permission denied: {0}")]
22    PermissionDenied(String),
23
24    /// Platform-specific error
25    #[error("Platform error: {0}")]
26    PlatformError(String),
27
28    /// Hardware acceleration not available
29    #[error("Hardware acceleration not available: {0}")]
30    HardwareAccelerationUnavailable(String),
31
32    /// Invalid configuration
33    #[error("Invalid configuration: {0}")]
34    InvalidConfiguration(String),
35
36    /// Memory allocation failed
37    #[error("Memory allocation failed: requested {size} bytes")]
38    MemoryAllocationFailed { size: usize },
39
40    /// Timeout occurred during capture
41    #[error("Capture timeout: exceeded {timeout_ms}ms")]
42    CaptureTimeout { timeout_ms: u64 },
43
44    /// IO error
45    #[error("IO error: {0}")]
46    IoError(#[from] std::io::Error),
47
48    /// Windows-specific error
49    #[cfg(windows)]
50    #[error("Windows error: {0}")]
51    WindowsError(#[from] windows::core::Error),
52
53    /// Encoding error
54    #[error("Encoding error: {0}")]
55    EncodingError(String),
56
57    /// Other errors
58    #[error(transparent)]
59    Other(#[from] anyhow::Error),
60}
61
62/// Error type for WebP encoding operations
63#[derive(Error, Debug)]
64pub enum EncodingError {
65    /// Invalid image dimensions
66    #[error("Invalid image dimensions: {width}x{height}")]
67    InvalidDimensions { width: u32, height: u32 },
68
69    /// Invalid pixel format
70    #[error("Invalid pixel format: {0}")]
71    InvalidPixelFormat(String),
72
73    /// Invalid configuration
74    #[error("Invalid configuration: {0}")]
75    InvalidConfiguration(String),
76
77    /// Unsupported format
78    #[error("Unsupported format: {0}")]
79    UnsupportedFormat(String),
80
81    /// Encoding failed
82    #[error("WebP encoding failed: {0}")]
83    EncodingFailed(String),
84
85    /// Invalid quality parameter
86    #[error("Invalid quality parameter: {0} (must be 0-100)")]
87    InvalidQuality(u8),
88
89    /// Invalid compression method
90    #[error("Invalid compression method: {0} (must be 0-6)")]
91    InvalidMethod(u8),
92
93    /// Buffer too small
94    #[error("Output buffer too small: need {required} bytes, got {provided}")]
95    BufferTooSmall { required: usize, provided: usize },
96
97    /// Unsupported feature
98    #[error("Unsupported feature: {0}")]
99    UnsupportedFeature(String),
100
101    /// Memory allocation failed
102    #[error("Memory allocation failed during encoding")]
103    MemoryAllocationFailed,
104
105    /// Other encoding errors
106    #[error(transparent)]
107    Other(#[from] anyhow::Error),
108}
109
110/// Error type for memory pool operations
111#[derive(Error, Debug)]
112pub enum MemoryPoolError {
113    /// Pool is full
114    #[error("Memory pool is full: max capacity {capacity} reached")]
115    PoolFull { capacity: usize },
116
117    /// Invalid buffer size
118    #[error("Invalid buffer size: {size}")]
119    InvalidBufferSize { size: usize },
120
121    /// Buffer not found in pool
122    #[error("Buffer not found in pool")]
123    BufferNotFound,
124
125    /// Pool is poisoned (mutex error)
126    #[error("Memory pool is poisoned")]
127    PoolPoisoned,
128}
129
130/// Combined result type for capture operations
131pub type CaptureResult<T> = Result<T, CaptureError>;
132
133/// Combined result type for encoding operations
134pub type EncodingResult<T> = Result<T, EncodingError>;
135
136/// Combined result type for memory pool operations
137pub type MemoryPoolResult<T> = Result<T, MemoryPoolError>;
138
139/// Convert Windows HRESULT to CaptureError
140#[cfg(windows)]
141pub fn from_hresult(hr: windows::core::HRESULT) -> CaptureError {
142    CaptureError::WindowsError(windows::core::Error::from(hr))
143}
144
145/// Convert error code to human-readable string
146pub fn error_code_to_string(code: i32) -> String {
147    match code {
148        -1 => "Generic error".to_string(),
149        -2 => "Invalid parameter".to_string(),
150        -3 => "Out of memory".to_string(),
151        -4 => "Not supported".to_string(),
152        -5 => "Permission denied".to_string(),
153        -6 => "Timeout".to_string(),
154        _ => format!("Unknown error code: {}", code),
155    }
156}
157
158impl CaptureError {
159    /// Check if the error is recoverable (worth retrying)
160    pub fn is_recoverable(&self) -> bool {
161        matches!(
162            self,
163            CaptureError::CaptureTimeout { .. } | CaptureError::MemoryAllocationFailed { .. }
164        )
165    }
166
167    /// Get error code for FFI
168    pub fn to_error_code(&self) -> i32 {
169        match self {
170            CaptureError::DisplayNotFound(_) => -1001,
171            CaptureError::DisplayEnumerationFailed(_) => -1002,
172            CaptureError::CaptureFailed(_) => -1003,
173            CaptureError::PermissionDenied(_) => -1004,
174            CaptureError::PlatformError(_) => -1005,
175            CaptureError::HardwareAccelerationUnavailable(_) => -1006,
176            CaptureError::InvalidConfiguration(_) => -1007,
177            CaptureError::MemoryAllocationFailed { .. } => -1008,
178            CaptureError::CaptureTimeout { .. } => -1009,
179            CaptureError::IoError(_) => -1010,
180            #[cfg(windows)]
181            CaptureError::WindowsError(_) => -1011,
182            CaptureError::EncodingError(_) => -1012,
183            CaptureError::Other(_) => -1999,
184        }
185    }
186}
187
188impl EncodingError {
189    /// Check if the error is related to invalid parameters
190    pub fn is_parameter_error(&self) -> bool {
191        matches!(
192            self,
193            EncodingError::InvalidDimensions { .. }
194                | EncodingError::InvalidPixelFormat(_)
195                | EncodingError::InvalidQuality(_)
196                | EncodingError::InvalidMethod(_)
197        )
198    }
199
200    /// Get error code for FFI
201    pub fn to_error_code(&self) -> i32 {
202        match self {
203            EncodingError::InvalidDimensions { .. } => -2001,
204            EncodingError::InvalidPixelFormat(_) => -2002,
205            EncodingError::InvalidConfiguration(_) => -2003,
206            EncodingError::UnsupportedFormat(_) => -2004,
207            EncodingError::EncodingFailed(_) => -2005,
208            EncodingError::InvalidQuality(_) => -2006,
209            EncodingError::InvalidMethod(_) => -2007,
210            EncodingError::BufferTooSmall { .. } => -2008,
211            EncodingError::UnsupportedFeature(_) => -2009,
212            EncodingError::MemoryAllocationFailed => -2010,
213            EncodingError::Other(_) => -2999,
214        }
215    }
216}
217
218impl From<EncodingError> for CaptureError {
219    fn from(err: EncodingError) -> Self {
220        CaptureError::EncodingError(err.to_string())
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn test_capture_error_display() {
230        let err = CaptureError::DisplayNotFound(2);
231        assert_eq!(err.to_string(), "Display not found: index 2");
232    }
233
234    #[test]
235    fn test_encoding_error_display() {
236        let err = EncodingError::InvalidQuality(150);
237        assert_eq!(
238            err.to_string(),
239            "Invalid quality parameter: 150 (must be 0-100)"
240        );
241    }
242
243    #[test]
244    fn test_error_code_conversion() {
245        let err = CaptureError::PermissionDenied("Screen recording".to_string());
246        assert_eq!(err.to_error_code(), -1004);
247    }
248
249    #[test]
250    fn test_is_recoverable() {
251        let timeout_err = CaptureError::CaptureTimeout { timeout_ms: 5000 };
252        assert!(timeout_err.is_recoverable());
253
254        let perm_err = CaptureError::PermissionDenied("test".to_string());
255        assert!(!perm_err.is_recoverable());
256    }
257}