Skip to main content

Module decode_side

Module decode_side 

Source
Expand description

Marking a Source as sitting BELOW a codec decoder.

§The problem this exists for

stuffr answers two different questions with the same Rust type. A read that fails because the bytes are malformed is the ARCHIVE’s fault — Error::Corrupt, exit 5. A read that fails because the disk, the pipe or the permission bits gave out is STUFFR’s environment failing — Error::Io, exit 1, which this project reserves for “stuffr itself failed”. Both arrive as an io::Error, and Error::from_decode_io exists to make the first call correctly.

What decides which one a given error is, is not the error’s kind — it is WHICH READ produced it. And until this module existed, the only thing that recorded that was whether the human who wrote the ? happened to remember. Every io::Error on every read path funnels through one conversion — impl From<io::Error> for Error — and that conversion could not see the difference, so a bare ? below a decoder was wrong by default.

It was wrong in four places at once, found by enumerating the funnel rather than by following a reproducer:

  • spill.rs’s two src.read(..)? calls — the ladder’s rung 3, reached by every needs_seek container (arj, zoo) over a codec.
  • lha.rs’s PeekSource::fill(source, 1)? in Lha::open.
  • cpio.rs’s fill_header_prefix()? per-header read-ahead.
  • zip.rs’s read_fixed/skip, whose map_err(Error::from) is the funnel spelled out longhand.

Three of those carry doc comments asserting the raw-source reading (“a genuine source failure keeps its own kind and exit code”) — correct for a raw source, false for a decoded one, and nothing in the type system disagreed.

§The mechanism

DecodeSideSource wraps a codec’s decoder output and tags every io::Error it yields with a private marker payload, preserving the error’s io::ErrorKind and its Display text exactly. From<io::Error> for Error then routes a tagged error through Error::from_decode_io and leaves an untagged one as Error::Io. A bare ? becomes correct on BOTH sides of the line, and stays correct for code nobody has written yet.

§Why this cannot lie in the opposite direction

Error::from_decode_io folds exactly two kinds — InvalidData to Corrupt and OutOfMemory to ResourceLimit. A real disk error, a permission failure or a broken pipe propagating up THROUGH a decoder keeps its own kind, so it is still Error::Io and still exit 1. The marker cannot turn an environment failure into “the archive is corrupt”; it can only stop a malformed-input report from claiming stuffr failed.

The raw source is never marked. resolve_chain_deep_with’s own top-level probe(src) call, Input::open, and SpillSource’s writes to its temp file all read or write something that is nobody’s decoder output, and all three still answer Error::Io. That is pinned from both ends by probe.rs’s a_raw_source_io_error_of_the_identical_kind_stays_io_not_ corrupt and by cli.rs’s list_reports_a_directory_as_an_io_failure_not_ as_corrupt.

Structs§

DecodeSideSource
A Source whose read failures are marked as decode-side.

Functions§

is_decode_side
Whether e came from a read below a codec decoder.
mark_decode_side
Tags e as having been raised by a read below a codec decoder.