mux/driver: map a mid-read halt to completed=false (Stop is not a failure)

The finish stage and the highway path already treat Error::Halted as a clean
operator stop (completed=false), but the header pump (`stream.read()?`) and the
frame pump (`Err(e) => return Err(e)`) propagated it as a hard error. Since slow
recovery reads dominate wall-clock, a Stop almost always lands mid-read, so it
surfaced as a mux failure instead of a resumable incomplete outcome — breaking the
driver's own "a clean operator stop is not an error" contract and diverging from
the ISO/Url highway (which returns Ok(None) on halt).

Both read arms now route Error::Halted to the completed=false path. Adds
error::is_halt(&io::Error) (typed, mirrors is_skippable_title_stub) as the check.
Two regression tests cover a halt landing mid-header-read and mid-frame-read.
This commit is contained in:
Matthew Jackson
2026-07-24 07:52:42 -07:00
parent e8c151792d
commit c00384d4df
2 changed files with 102 additions and 1 deletions
+9
View File
@@ -912,6 +912,15 @@ pub fn is_skippable_title_stub(e: &std::io::Error) -> bool {
matches!(io_error_code(e), Some(E_MKV_INVALID | E_CSS_KEY_MISSING))
}
/// Whether an [`io::Error`](std::io::Error) is a cooperative user stop
/// ([`Error::Halted`], code [`E_HALTED`]) — vs a structural failure. A stop is
/// resumable, not a rip failure: `mux_stream` maps a mid-run halt to
/// `completed = false`, and consumers preserve staging rather than quarantining.
/// Typed replacement for the consumers' `E<code>`-leading-token string match.
pub fn is_halt(e: &std::io::Error) -> bool {
io_error_code(e) == Some(E_HALTED)
}
impl Error {
/// Borrow the drive-returned SPC-4 sense triple if this error is a
/// [`Error::ScsiError`] carrying sense data. `None` for any other