Reword FR-RAW-2's mechanism to the one that serves its purpose

FR-RAW-2 required RAW decoding to sit behind "a trait taking a SourceRef, not a
filesystem path". The purpose is met — `dr_decode::decode` takes `&[u8]` and
the crate has no path-based entry point anywhere — and the mechanism is not,
because a decoder taking a SourceRef would be a worse design than the one built.

A SourceRef is opaque. The only thing that turns one into bytes is `Storage`,
which lives in platform/dr-plat, so a decoder taking a SourceRef must take a
Storage with it: retry, permission loss and remote fetching move inside the
decoder, and the decoder becomes constructible only where a Storage exists.
Bytes in, image out, is narrower and more portable — the decoder cannot know
where its input came from, which is the property the requirement wants.

The Nextcloud case is the one a byte-oriented API looks like it should lose,
and it is the clearest illustration that it does not. `dr_decode::HEADER_BYTES`
declares how much of a file the decoder needs in order to read metadata, and
`import.rs` fetches exactly that range through `Storage::read_range` before
calling `dr_decode::metadata`. The decoder states a requirement; the storage
layer satisfies it. A decoder holding its own SourceRef would have had to carry
the range policy itself.

The trait half of the clause is left standing and unmet. There is one decoder
reached through free functions, so "a second implementation may be added
without changing callers" is still outstanding work rather than a description.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 10:22:22 +02:00
co-authored by Claude Opus 5
parent 11e515a295
commit e0da93c59a
+28 -4
View File
@@ -206,10 +206,34 @@ destructive action, since that figure is what tells the user whether they meant
CR3), Nikon (NEF), Sony (ARW), Fujifilm (RAF, including X-Trans), Panasonic (RW2), Olympus (ORF),
Adobe DNG. Additional formats are a coverage goal, not a launch blocker.
**FR-RAW-2 — Decoder abstraction.** RAW decoding sits behind a trait taking a `SourceRef`
(FR-CAT-1a), not a filesystem path, so the same decoder works over a local file, an Android SAF
document, or a byte range fetched from Nextcloud. A second implementation may be added for broader
camera coverage without changing callers (D2).
**FR-RAW-2 — Decoder abstraction.** RAW decoding takes **bytes**, never a filesystem path and never
a reference it would have to resolve. Resolving a `SourceRef` (FR-CAT-1a) to bytes is
`Storage::open`'s job and happens at the caller, so the same decoder works over a local file, an
Android SAF document, or a byte range fetched from Nextcloud. A second implementation may be added
for broader camera coverage without changing callers (D2).
*On the change of mechanism.* This clause used to require "a trait taking a `SourceRef`". The
purpose — that no decoder API takes a path, so nothing in the decode path assumes a filesystem —
is met and is not in question: `dr_decode::decode` takes `&[u8]`, and there is no path-based entry
point in the crate. The mechanism was wrong, and stating it that way would have made the design
worse.
A `SourceRef` is opaque by construction; the only thing that turns one into readable bytes is
`Storage`, in `platform/dr-plat`. A decoder taking a `SourceRef` would therefore have to take a
`Storage` alongside it, which moves retry, permission loss and remote fetching inside the decoder
and leaves it constructible only where a `Storage` exists. Bytes in, image out, is both narrower and
more portable: the decoder has no idea where its input came from, which is the property this
requirement is actually asking for.
It also serves the Nextcloud case better rather than worse, which is the one a byte-oriented API
looks like it would lose. `dr_decode::HEADER_BYTES` declares how much of a file the decoder needs to
read metadata, and `import.rs` fetches exactly that range through `Storage::read_range` before
calling `dr_decode::metadata`. The decoder states its requirement and the storage layer satisfies
it; a decoder holding its own `SourceRef` would have had to implement the range policy itself.
What is genuinely not built is the trait. There is one decoder, reached through free functions, so
"without changing callers" is a claim nothing yet tests. The clause stands as written and is
outstanding work, not a satisfied one.
**FR-RAW-3 — Sensor data handling.** Correctly apply per-camera black/white levels, CFA pattern
identification, and camera-native colour matrices. Demosaic quality shall be selectable, with at