scverse / scverse/rustar-aligner
Read other compressed input formats (bz2 / zstd / xz): evaluate niffler
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 75
- Forks
- 7
- Avg merge
- 8m
- Merged PRs (30d)
- 1
Description
Dependency discussion per CONTRIBUTING.md, sub-issue of #201.
Goal
Accept compressed FASTQ input beyond gzip: bzip2, xz, zstd, without the user having to route it
through --readFilesCommand.
Where we are today
src/io/fastq.rs:88-110:
- compression is detected by file extension (
.gz/.gzip), not by content - the only decoder is gzip, via
flate2::read::GzDecoder - anything else has to go through
--readFilesCommand(external process, as STAR does)
Two consequences: a .fq.zst is read as plain text and fails on garbage, and a gzip file with an
unusual name silently gets the same treatment. Related: the single-member gzip bug filed alongside
this issue.
Candidate
niffler 3.0.1, MIT/Apache-2.0, 532k downloads, released
2026-04-29.
- Detection by magic bytes, not extension:
bytes2typepeeks 5 bytes and matches1f 8b
(gzip),42 5a(bzip2),28 b5 2f fd(zstd),fd 37 7a 58 5a(xz), falling back to
Format::No= pass through as plain. - Gzip goes through
flate2::read::MultiGzDecoder(src/basic/compression.rs:54), so adopting it
fixes the multi-member truncation bug by construction. niffler::send::get_reader/from_pathreturnBox<dyn Read + Send>, which drops straight into
the existingBox<dyn BufRead + Send>inFastqReader::openvia aBufReader::with_capacity.
The part that needs a decision: backends
niffler's default feature set is bgz + bz2 + gz + lzma + zstd, each with the upstream
crate's default features. That pulls in four more codecs, and for a crate published to crates.io
and built on five platforms including Windows, the backend of each is the whole question:
| Format | niffler dep | Backend reality |
|---|---|---|
| gz | flate2 |
Already in the tree. niffler declares it default-features = false, so our zlib-rs choice survives feature unification |
| bz2 | bzip2 0.6 |
Pure Rust by default: bzip2's own default = ["dep:libbz2-rs-sys"], with the C bzip2-sys as an opt-in. Good news |
| zstd | zstd 0.13 |
C bindings (zstd-sys). Pure-Rust decode-only alternative exists: ruzstd 0.9.0, active, 57M downloads. niffler does not offer it as a backend |
| xz | liblzma 0.4 |
C, non-optional liblzma-sys. Pure-Rust alternative lzma-rs is at 0.3.0 from 2023 |
| bgz | bgzip 0.3.1 |
Stale (2023) and redundant: we already depend on noodles-bgzf 0.51 |
So the recommended shape if we adopt it is explicit, not default:
niffler = { version = "3", default-features = false, features = ["gz", "bz2"] }
and then a deliberate decision on zstd (C zstd via niffler, or ruzstd wired in ourselves) and on
xz (C only, in practice).
Alternative: do the sniff in-tree
The sniff is a 5-byte peek and a match; the decoders are one crate each. In-tree we could pick
ruzstd for pure-Rust zstd decode, which niffler cannot give us. Against that: niffler is 532k
downloads of battle-testing on exactly this problem, including the Send plumbing and the
pass-through case, and it is one dependency instead of three.
Reasonable split: take niffler for detection + gz + bz2, and treat zstd as its own decision.
STAR-compatibility note
STAR itself does not sniff; it expects --readFilesCommand (zcat, bunzip2, …). Auto-detection
is already a local convenience for gzip, so extending it is an extension of an existing divergence,
not a new one. Worth a line in DIVERGENCE.md and maintainer sign-off, since it changes what input
the tool silently accepts.
Checklist
- Decide the format list actually wanted: bz2 and zstd are the realistic ones, xz is rare
- Decide zstd backend: C
zstd(via niffler) vs pure-Rustruzstd(decode-only, in-tree) - Confirm no C toolchain requirement is introduced on any of the five CI platforms
- Explicitly disable niffler's
bgzfeature; BGZF stays onnoodles-bgzf - Keep
--readFilesCommandworking and taking precedence over sniffing - Detection must work on stdin/pipes too, or be documented as file-only
- Tests: one fixture per accepted format, plus a multi-member gzip case
-
DIVERGENCE.mdentry + sign-off for the auto-detection behaviour
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at src/io/fastq.rs:88-110 and review the dependency guidance in CONTRIBUTING.md. Evaluate the niffler feature and backend choices against the five CI platforms, stdin and --readFilesCommand behavior, and the listed compression fixtures. Done means the format and backend decisions are settled, tests cover each accepted format and multi-member gzip, and DIVERGENCE.md plus maintainer sign-off are addressed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- bioinformatics
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100