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 twosrc.read(..)?calls — the ladder’s rung 3, reached by everyneeds_seekcontainer (arj,zoo) over a codec.lha.rs’sPeekSource::fill(source, 1)?inLha::open.cpio.rs’sfill_header_prefix()?per-header read-ahead.zip.rs’sread_fixed/skip, whosemap_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§
- Decode
Side Source - A
Sourcewhose read failures are marked as decode-side.
Functions§
- is_
decode_ side - Whether
ecame from a read below a codec decoder. - mark_
decode_ side - Tags
eas having been raised by a read below a codec decoder.