webp_screenshot_rust/
error.rs1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum CaptureError {
8 #[error("Display not found: index {0}")]
10 DisplayNotFound(usize),
11
12 #[error("Failed to enumerate displays: {0}")]
14 DisplayEnumerationFailed(String),
15
16 #[error("Capture failed: {0}")]
18 CaptureFailed(String),
19
20 #[error("Permission denied: {0}")]
22 PermissionDenied(String),
23
24 #[error("Platform error: {0}")]
26 PlatformError(String),
27
28 #[error("Hardware acceleration not available: {0}")]
30 HardwareAccelerationUnavailable(String),
31
32 #[error("Invalid configuration: {0}")]
34 InvalidConfiguration(String),
35
36 #[error("Memory allocation failed: requested {size} bytes")]
38 MemoryAllocationFailed { size: usize },
39
40 #[error("Capture timeout: exceeded {timeout_ms}ms")]
42 CaptureTimeout { timeout_ms: u64 },
43
44 #[error("IO error: {0}")]
46 IoError(#[from] std::io::Error),
47
48 #[cfg(windows)]
50 #[error("Windows error: {0}")]
51 WindowsError(#[from] windows::core::Error),
52
53 #[error("Encoding error: {0}")]
55 EncodingError(String),
56
57 #[error(transparent)]
59 Other(#[from] anyhow::Error),
60}
61
62#[derive(Error, Debug)]
64pub enum EncodingError {
65 #[error("Invalid image dimensions: {width}x{height}")]
67 InvalidDimensions { width: u32, height: u32 },
68
69 #[error("Invalid pixel format: {0}")]
71 InvalidPixelFormat(String),
72
73 #[error("Invalid configuration: {0}")]
75 InvalidConfiguration(String),
76
77 #[error("Unsupported format: {0}")]
79 UnsupportedFormat(String),
80
81 #[error("WebP encoding failed: {0}")]
83 EncodingFailed(String),
84
85 #[error("Invalid quality parameter: {0} (must be 0-100)")]
87 InvalidQuality(u8),
88
89 #[error("Invalid compression method: {0} (must be 0-6)")]
91 InvalidMethod(u8),
92
93 #[error("Output buffer too small: need {required} bytes, got {provided}")]
95 BufferTooSmall { required: usize, provided: usize },
96
97 #[error("Unsupported feature: {0}")]
99 UnsupportedFeature(String),
100
101 #[error("Memory allocation failed during encoding")]
103 MemoryAllocationFailed,
104
105 #[error(transparent)]
107 Other(#[from] anyhow::Error),
108}
109
110#[derive(Error, Debug)]
112pub enum MemoryPoolError {
113 #[error("Memory pool is full: max capacity {capacity} reached")]
115 PoolFull { capacity: usize },
116
117 #[error("Invalid buffer size: {size}")]
119 InvalidBufferSize { size: usize },
120
121 #[error("Buffer not found in pool")]
123 BufferNotFound,
124
125 #[error("Memory pool is poisoned")]
127 PoolPoisoned,
128}
129
130pub type CaptureResult<T> = Result<T, CaptureError>;
132
133pub type EncodingResult<T> = Result<T, EncodingError>;
135
136pub type MemoryPoolResult<T> = Result<T, MemoryPoolError>;
138
139#[cfg(windows)]
141pub fn from_hresult(hr: windows::core::HRESULT) -> CaptureError {
142 CaptureError::WindowsError(windows::core::Error::from(hr))
143}
144
145pub 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 pub fn is_recoverable(&self) -> bool {
161 matches!(
162 self,
163 CaptureError::CaptureTimeout { .. } | CaptureError::MemoryAllocationFailed { .. }
164 )
165 }
166
167 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 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 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}