Skip to main content

cd_da_reader/
errors.rs

1use std::fmt;
2
3/// SCSI command groups issued by this library.
4#[derive(Debug, Clone, Copy)]
5pub enum ScsiOp {
6    /// `READ TOC/PMA/ATIP` command (opcode `0x43`) for TOC/session metadata.
7    ReadToc,
8    /// `READ CD` command (opcode `0xBE`) for CD-DA sector payload (2352 bytes/sector).
9    ReadCd,
10    /// `READ SUB-CHANNEL` command for Q-channel/subcode metadata.
11    ReadSubChannel,
12}
13
14/// Structured SCSI failure context captured at the call site.
15///
16/// This keeps transport/protocol details (status + sense) separate from plain I/O failures,
17/// which allows retry logic and application diagnostics to branch on SCSI metadata.
18#[derive(Debug, Clone)]
19pub struct ScsiError {
20    /// Operation that failed.
21    pub op: ScsiOp,
22    /// Starting logical block address used by the failed command, when applicable.
23    pub lba: Option<u32>,
24    /// Sector count requested by the failed command, when applicable.
25    pub sectors: Option<u32>,
26    /// SCSI status byte reported by the device (for example `0x02` for CHECK CONDITION).
27    pub scsi_status: u8,
28    /// Sense key nibble from fixed-format sense data (if sense data was returned).
29    pub sense_key: Option<u8>,
30    /// Additional Sense Code from sense data (if available).
31    pub asc: Option<u8>,
32    /// Additional Sense Code Qualifier paired with `asc` (if available).
33    pub ascq: Option<u8>,
34}
35
36/// Top-level error type returned by `cd-da-reader`.
37#[derive(Debug)]
38pub enum CdReaderError {
39    /// OS/transport I/O error (open/ioctl/DeviceIoControl/FFI command failure, etc.).
40    Io(std::io::Error),
41    /// Device reported a SCSI command failure with status/sense context.
42    Scsi(ScsiError),
43    /// Parsing failure for command payloads (TOC/CD-TEXT/subchannel parsing).
44    Parse(String),
45}
46
47impl fmt::Display for CdReaderError {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        match self {
50            Self::Io(err) => write!(f, "io error: {err}"),
51            Self::Scsi(err) => write!(
52                f,
53                "SCSI {:?} failed (status=0x{:02x}, lba={:?}, sectors={:?}, sense_key={:?}, asc={:?}, ascq={:?})",
54                err.op, err.scsi_status, err.lba, err.sectors, err.sense_key, err.asc, err.ascq
55            ),
56            Self::Parse(msg) => write!(f, "parse error: {msg}"),
57        }
58    }
59}
60
61impl std::error::Error for CdReaderError {}
62
63impl From<std::io::Error> for CdReaderError {
64    fn from(value: std::io::Error) -> Self {
65        Self::Io(value)
66    }
67}