use thiserror::Error;
#[derive(Error, Debug)]
pub enum CaptureError {
#[error("Display not found: index {0}")]
DisplayNotFound(usize),
#[error("Failed to enumerate displays: {0}")]
DisplayEnumerationFailed(String),
#[error("Capture failed: {0}")]
CaptureFailed(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("Platform error: {0}")]
PlatformError(String),
#[error("Hardware acceleration not available: {0}")]
HardwareAccelerationUnavailable(String),
#[error("Invalid configuration: {0}")]
InvalidConfiguration(String),
#[error("Memory allocation failed: requested {size} bytes")]
MemoryAllocationFailed { size: usize },
#[error("Capture timeout: exceeded {timeout_ms}ms")]
CaptureTimeout { timeout_ms: u64 },
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[cfg(windows)]
#[error("Windows error: {0}")]
WindowsError(#[from] windows::core::Error),
#[error("Encoding error: {0}")]
EncodingError(String),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
#[derive(Error, Debug)]
pub enum EncodingError {
#[error("Invalid image dimensions: {width}x{height}")]
InvalidDimensions { width: u32, height: u32 },
#[error("Invalid pixel format: {0}")]
InvalidPixelFormat(String),
#[error("Invalid configuration: {0}")]
InvalidConfiguration(String),
#[error("Unsupported format: {0}")]
UnsupportedFormat(String),
#[error("WebP encoding failed: {0}")]
EncodingFailed(String),
#[error("Invalid quality parameter: {0} (must be 0-100)")]
InvalidQuality(u8),
#[error("Invalid compression method: {0} (must be 0-6)")]
InvalidMethod(u8),
#[error("Output buffer too small: need {required} bytes, got {provided}")]
BufferTooSmall { required: usize, provided: usize },
#[error("Unsupported feature: {0}")]
UnsupportedFeature(String),
#[error("Memory allocation failed during encoding")]
MemoryAllocationFailed,
#[error(transparent)]
Other(#[from] anyhow::Error),
}
#[derive(Error, Debug)]
pub enum MemoryPoolError {
#[error("Memory pool is full: max capacity {capacity} reached")]
PoolFull { capacity: usize },
#[error("Invalid buffer size: {size}")]
InvalidBufferSize { size: usize },
#[error("Buffer not found in pool")]
BufferNotFound,
#[error("Memory pool is poisoned")]
PoolPoisoned,
}
pub type CaptureResult<T> = Result<T, CaptureError>;
pub type EncodingResult<T> = Result<T, EncodingError>;
pub type MemoryPoolResult<T> = Result<T, MemoryPoolError>;
#[cfg(windows)]
pub fn from_hresult(hr: windows::core::HRESULT) -> CaptureError {
CaptureError::WindowsError(windows::core::Error::from(hr))
}
pub fn error_code_to_string(code: i32) -> String {
match code {
-1 => "Generic error".to_string(),
-2 => "Invalid parameter".to_string(),
-3 => "Out of memory".to_string(),
-4 => "Not supported".to_string(),
-5 => "Permission denied".to_string(),
-6 => "Timeout".to_string(),
_ => format!("Unknown error code: {}", code),
}
}
impl CaptureError {
pub fn is_recoverable(&self) -> bool {
matches!(
self,
CaptureError::CaptureTimeout { .. } | CaptureError::MemoryAllocationFailed { .. }
)
}
pub fn to_error_code(&self) -> i32 {
match self {
CaptureError::DisplayNotFound(_) => -1001,
CaptureError::DisplayEnumerationFailed(_) => -1002,
CaptureError::CaptureFailed(_) => -1003,
CaptureError::PermissionDenied(_) => -1004,
CaptureError::PlatformError(_) => -1005,
CaptureError::HardwareAccelerationUnavailable(_) => -1006,
CaptureError::InvalidConfiguration(_) => -1007,
CaptureError::MemoryAllocationFailed { .. } => -1008,
CaptureError::CaptureTimeout { .. } => -1009,
CaptureError::IoError(_) => -1010,
#[cfg(windows)]
CaptureError::WindowsError(_) => -1011,
CaptureError::EncodingError(_) => -1012,
CaptureError::Other(_) => -1999,
}
}
}
impl EncodingError {
pub fn is_parameter_error(&self) -> bool {
matches!(
self,
EncodingError::InvalidDimensions { .. }
| EncodingError::InvalidPixelFormat(_)
| EncodingError::InvalidQuality(_)
| EncodingError::InvalidMethod(_)
)
}
pub fn to_error_code(&self) -> i32 {
match self {
EncodingError::InvalidDimensions { .. } => -2001,
EncodingError::InvalidPixelFormat(_) => -2002,
EncodingError::InvalidConfiguration(_) => -2003,
EncodingError::UnsupportedFormat(_) => -2004,
EncodingError::EncodingFailed(_) => -2005,
EncodingError::InvalidQuality(_) => -2006,
EncodingError::InvalidMethod(_) => -2007,
EncodingError::BufferTooSmall { .. } => -2008,
EncodingError::UnsupportedFeature(_) => -2009,
EncodingError::MemoryAllocationFailed => -2010,
EncodingError::Other(_) => -2999,
}
}
}
impl From<EncodingError> for CaptureError {
fn from(err: EncodingError) -> Self {
CaptureError::EncodingError(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_capture_error_display() {
let err = CaptureError::DisplayNotFound(2);
assert_eq!(err.to_string(), "Display not found: index 2");
}
#[test]
fn test_encoding_error_display() {
let err = EncodingError::InvalidQuality(150);
assert_eq!(
err.to_string(),
"Invalid quality parameter: 150 (must be 0-100)"
);
}
#[test]
fn test_error_code_conversion() {
let err = CaptureError::PermissionDenied("Screen recording".to_string());
assert_eq!(err.to_error_code(), -1004);
}
#[test]
fn test_is_recoverable() {
let timeout_err = CaptureError::CaptureTimeout { timeout_ms: 5000 };
assert!(timeout_err.is_recoverable());
let perm_err = CaptureError::PermissionDenied("test".to_string());
assert!(!perm_err.is_recoverable());
}
}