//! Error types for the geek-szitman-supercamera crate use thiserror::Error; /// Result type for the crate pub type Result = std::result::Result; /// Main error type for the crate #[derive(Error, Debug)] pub enum Error { /// USB communication errors #[error("USB error: {0}")] Usb(#[from] UsbError), /// Video backend errors #[error("Video backend error: {0}")] Video(#[from] VideoError), /// Protocol errors #[error("Protocol error: {0}")] Protocol(#[from] ProtocolError), /// JPEG processing errors #[error("JPEG error: {0}")] Jpeg(#[from] JpegError), /// System errors #[error("System error: {0}")] System(#[from] SystemError), /// Generic error wrapper #[error("Generic error: {0}")] Generic(String), } /// USB-specific errors #[derive(Error, Debug)] pub enum UsbError { /// Device not found #[error("USB device not found")] DeviceNotFound, /// Device disconnected #[error("USB device disconnected")] DeviceDisconnected, /// Permission denied #[error("USB permission denied")] PermissionDenied, /// Interface claim failed #[error("Failed to claim USB interface: {0}")] InterfaceClaimFailed(String), /// Bulk transfer failed #[error("USB bulk transfer failed: {0}")] BulkTransferFailed(String), /// Timeout error #[error("USB operation timed out")] Timeout, /// Generic USB error #[error("USB error: {0}")] Generic(String), } impl UsbError { /// Check if this error indicates device disconnection pub fn is_device_disconnected(&self) -> bool { matches!(self, Self::DeviceDisconnected) } } /// Video backend errors #[derive(Error, Debug)] pub enum VideoError { /// PipeWire errors #[error("PipeWire error: {0}")] PipeWire(String), /// V4L2 errors (for future use) #[error("V4L2 error: {0}")] V4L2(String), /// Stdout errors #[error("Stdout error: {0}")] Stdout(String), /// Format not supported #[error("Video format not supported: {0}")] FormatNotSupported(String), /// Device initialization failed #[error("Video device initialization failed: {0}")] InitializationFailed(String), /// Frame push failed #[error("Failed to push frame: {0}")] FramePushFailed(String), /// Device not ready #[error("Device not ready")] DeviceNotReady, } /// Protocol errors #[derive(Error, Debug)] pub enum ProtocolError { /// Invalid frame format #[error("Invalid frame format: {0}")] InvalidFrameFormat(String), /// Frame too small #[error( "Frame too small: expected at least {} bytes, got {}", expected, actual )] FrameTooSmall { expected: usize, actual: usize }, /// Invalid magic number #[error( "Invalid magic number: expected 0x{:04X}, got 0x{:04X}", expected, actual )] InvalidMagic { expected: u16, actual: u16 }, /// Unknown camera ID #[error("Unknown camera ID: {0}")] UnknownCameraId(u8), /// Frame length mismatch #[error("Frame length mismatch: expected {}, got {}", expected, actual)] FrameLengthMismatch { expected: usize, actual: usize }, /// Protocol parsing error #[error("Protocol parsing error: {0}")] ParsingError(String), } /// JPEG processing errors #[derive(Error, Debug)] pub enum JpegError { /// Invalid JPEG header #[error("Invalid JPEG header")] InvalidHeader, /// Unsupported JPEG format #[error("Unsupported JPEG format: {0}")] UnsupportedFormat(String), /// JPEG parsing failed #[error("JPEG parsing failed: {0}")] ParsingFailed(String), /// Image dimensions not found #[error("Could not determine image dimensions")] DimensionsNotFound, } /// System errors #[derive(Error, Debug)] pub enum SystemError { /// File operation failed #[error("File operation failed: {0}")] FileError(String), /// Permission denied #[error("Permission denied: {0}")] PermissionDenied(String), /// Resource not available #[error("Resource not available: {0}")] ResourceNotAvailable(String), /// Signal handling error #[error("Signal handling error: {0}")] SignalError(String), } impl From for Error { fn from(err: std::io::Error) -> Self { match err.kind() { std::io::ErrorKind::NotFound => Error::Usb(UsbError::DeviceNotFound), std::io::ErrorKind::PermissionDenied => Error::Usb(UsbError::PermissionDenied), std::io::ErrorKind::TimedOut => Error::Usb(UsbError::Timeout), _ => Error::System(SystemError::FileError(err.to_string())), } } } impl From for Error { fn from(err: rusb::Error) -> Self { match err { rusb::Error::NoDevice => Error::Usb(UsbError::DeviceDisconnected), rusb::Error::Access => Error::Usb(UsbError::PermissionDenied), rusb::Error::Timeout => Error::Usb(UsbError::Timeout), rusb::Error::NotFound => Error::Usb(UsbError::DeviceNotFound), _ => Error::Usb(UsbError::Generic(err.to_string())), } } } impl From for Error { fn from(err: String) -> Self { Error::Generic(err) } } impl From<&str> for Error { fn from(err: &str) -> Self { Error::Generic(err.to_string()) } } impl From for Error { fn from(err: crate::usb::UsbTransferError) -> Self { Error::Usb(UsbError::Generic(err.to_string())) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_usb_error_is_device_disconnected() { let err = UsbError::DeviceDisconnected; assert!(err.is_device_disconnected()); let err = UsbError::DeviceNotFound; assert!(!err.is_device_disconnected()); } #[test] fn test_error_conversion() { let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "not found"); let err: Error = io_err.into(); assert!(matches!(err, Error::Usb(UsbError::DeviceNotFound))); let usb_err = rusb::Error::NoDevice; let err: Error = usb_err.into(); assert!(matches!(err, Error::Usb(UsbError::DeviceDisconnected))); } }