summaryrefslogtreecommitdiff
path: root/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/error.rs')
-rw-r--r--src/error.rs247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..f101a61
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,247 @@
+//! Error types for the geek-szitman-supercamera crate
+
+use thiserror::Error;
+
+/// Result type for the crate
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// 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<std::io::Error> 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<rusb::Error> 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<String> 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<crate::usb::UsbTransferError> 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)));
+ }
+}