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}