diff --git a/src/error.rs b/src/error.rs index 19254a8..4f06d44 100644 --- a/src/error.rs +++ b/src/error.rs @@ -929,6 +929,21 @@ pub fn is_halt(e: &std::io::Error) -> bool { io_error_code(e) == Some(E_HALTED) } +/// Whether an [`io::Error`](std::io::Error) is a **disc-level** key failure — +/// the disc as a whole cannot be decrypted, so EVERY title will fail the same +/// way. Distinct from a per-title skippable stub +/// ([`is_skippable_title_stub`]): `E_NO_DISC_KEY` (keydb present but no entry +/// for this disc), `E_KEYDB_LOAD` (no keydb at all), and `E_AACS_NO_KEYS` (no +/// usable AACS key material) are all whole-disc conditions. A multi-title rip +/// loop should stop immediately on this (fail-fast) rather than iterate every +/// title re-printing the same error. +pub fn is_disc_level_no_key(e: &std::io::Error) -> bool { + matches!( + io_error_code(e), + Some(E_NO_DISC_KEY | E_KEYDB_LOAD | E_AACS_NO_KEYS) + ) +} + impl Error { /// Borrow the drive-returned SPC-4 sense triple if this error is a /// [`Error::ScsiError`] carrying sense data. `None` for any other diff --git a/src/lib.rs b/src/lib.rs index e2e24c6..96bae02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,7 +155,7 @@ pub use session::{ // All fallible APIs return `Result`. `Error` is a typed enum with a // numeric `code()`; **no English text in the library** — applications map // codes to localized messages. See `error.rs` for the full taxonomy. -pub use error::{Error, Result, is_halt, is_skippable_title_stub}; +pub use error::{Error, Result, is_disc_level_no_key, is_halt, is_skippable_title_stub}; // ─── Cooperative cancellation ─────────────────────────────────────────────── //