You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 6.3 of #28: ship a zstd CLI binary with full upstream v1.5.7 command-line parity. Single binary; downstream packagers create the conventional symlinks (zstdcat, unzstd, zstdmt) and our binary detects them via argv[0] and adjusts behaviour, matching upstream's dispatch.
Deliverables
1. New workspace member cli/ (binary crate)
[package]
name = "structured-zstd-cli"# ...
[[bin]]
name = "zstd"path = "src/main.rs"
Default-builds an optimized binary; downstream packagers strip + install to /usr/bin/zstd and ln -s zstd /usr/bin/{zstdcat,unzstd,zstdmt,zstdgrep,zstdless}.
2. CLI dispatch by argv[0]
Upstream behaviour replicated:
zstd: default — compresses by default
zstdcat / zstdcat.exe: decompresses to stdout
unzstd: decompresses, output to file
zstdmt: compresses with multi-threading (-T0 default, max threads)
zstdgrep: decompresses + pipes to grep
zstdless: decompresses + pipes to less (sets LESSOPEN)
3. Command-line surface
Full parity with upstream zstd --help output. Argument parser uses clap v4 with custom builder mode (not derive — upstream's help text is hand-formatted and we need byte-similar output for --help/--version).
-T#, --threads=N — handled here as parsing + forwarding to ZSTD_c_nbWorkers. Actual MT impl lives in 6.4 (chore: release v0.0.7 #64).
--single-thread, --adapt, --adapt=min=N,max=N
Format toggles (for non-zstd outputs upstream's CLI also handles):
--format=zstd / gzip / xz / lz4 — this issue implements zstd only; the other formats are deferred to a future issue (out of scope for drop-in since Alpine zstd package's gzip/xz/lz4 modes are optional).
Environment variables:
ZSTD_CLEVEL — default compression level
ZSTD_NBTHREADS — default thread count
ZSTD_NB_WORKERS — alias for ZSTD_NBTHREADS
4. File operations
Read regular file, stat preserved (mtime, mode propagated to output on --keep).
Multi-file mode: zstd a b c compresses each individually to a.zst, b.zst, c.zst.
Glob-like: not handled by us (shell expands).
Output to - means stdout regardless of where positional args resolve.
Error on existing output without -f; refuses to write compressed to a TTY without -f.
--rm deletes source after successful operation.
5. Help/version output matching
--version output: zstd version 1.5.7 (structured-zstd v0.0.X) — first line matches upstream prefix, second line identifies our impl. --help body matches upstream text where applicable (sections, flag descriptions). Where our impl diverges from upstream behaviour (e.g. no MT in 6.3, no legacy decode in 6.3), the help text spells that out explicitly so users aren't surprised.
6. Tests (cli/tests/)
cli/tests/integration.rs — drives the binary via assert_cmd for happy-path scenarios (compress/decompress/test/list, stdin/stdout, multi-file).
cli/tests/argv0_dispatch.rs — symlinks target/debug/zstd to zstdcat/unzstd/zstdmt and verifies the dispatch.
cli/tests/help_snapshot.rs — snapshot --help output so accidental changes show up in review.
Multi-threaded compression itself → 6.4 (chore: release v0.0.7 #64). This issue parses -T# and forwards via ZSTD_c_nbWorkers; if 6.4 is not merged yet, -T0/-T1 work via single-threaded fallback.
Summary
Phase 6.3 of #28: ship a
zstdCLI binary with full upstream v1.5.7 command-line parity. Single binary; downstream packagers create the conventional symlinks (zstdcat,unzstd,zstdmt) and our binary detects them viaargv[0]and adjusts behaviour, matching upstream's dispatch.Deliverables
1. New workspace member
cli/(binary crate)Default-builds an
optimizedbinary; downstream packagers strip + install to/usr/bin/zstdandln -s zstd /usr/bin/{zstdcat,unzstd,zstdmt,zstdgrep,zstdless}.2. CLI dispatch by
argv[0]Upstream behaviour replicated:
zstd: default — compresses by defaultzstdcat/zstdcat.exe: decompresses to stdoutunzstd: decompresses, output to filezstdmt: compresses with multi-threading (-T0default, max threads)zstdgrep: decompresses + pipes togrepzstdless: decompresses + pipes toless(setsLESSOPEN)3. Command-line surface
Full parity with upstream
zstd --helpoutput. Argument parser usesclapv4 with custom builder mode (not derive — upstream's help text is hand-formatted and we need byte-similar output for--help/--version).Mode flags:
-z,--compress(default unlessargv[0]says otherwise)-d,--decompress,--uncompress-t,--test-l,--list-b,--benchmark(with-b#,-e#,-i#,-B#,-Sfollow-ups)Compression level:
-1through-19(literal flags)--fast/--fast=N(N in 1..7)--ultra(unlocks-20..-22)-20/-21/-22only valid with--ultra--long=N(LDM, requires feat: long distance matching (LDM) for compression #18 internal infra)--no-content-size,--no-check,--no-dictIDI/O:
-c,--stdout,--to-stdout-o FILE,--output FILE-f,--force-k,--keep--rm--no-progress-.zst(compress), strip.zst(decompress)Dictionary:
-D dictfile,--use-dict=FILE--train,--train-fastcover,--maxdict=N,--dictID=NVerbosity:
-q,--quiet-v,--verbose-vv,-vvv(extra debug)--version,-V--help,-h,-H(long help)Multi-thread (delegated to 6.4):
-T#,--threads=N— handled here as parsing + forwarding toZSTD_c_nbWorkers. Actual MT impl lives in 6.4 (chore: release v0.0.7 #64).--single-thread,--adapt,--adapt=min=N,max=NFormat toggles (for non-zstd outputs upstream's CLI also handles):
--format=zstd/gzip/xz/lz4— this issue implementszstdonly; the other formats are deferred to a future issue (out of scope for drop-in since Alpinezstdpackage's gzip/xz/lz4 modes are optional).Environment variables:
ZSTD_CLEVEL— default compression levelZSTD_NBTHREADS— default thread countZSTD_NB_WORKERS— alias forZSTD_NBTHREADS4. File operations
--keep).zstd a b ccompresses each individually toa.zst,b.zst,c.zst.-means stdout regardless of where positional args resolve.-f; refuses to write compressed to a TTY without-f.--rmdeletes source after successful operation.5. Help/version output matching
--versionoutput:zstd version 1.5.7 (structured-zstd v0.0.X)— first line matches upstream prefix, second line identifies our impl.--helpbody matches upstream text where applicable (sections, flag descriptions). Where our impl diverges from upstream behaviour (e.g. no MT in 6.3, no legacy decode in 6.3), the help text spells that out explicitly so users aren't surprised.6. Tests (
cli/tests/)cli/tests/integration.rs— drives the binary viaassert_cmdfor happy-path scenarios (compress/decompress/test/list, stdin/stdout, multi-file).cli/tests/argv0_dispatch.rs— symlinkstarget/debug/zstdtozstdcat/unzstd/zstdmtand verifies the dispatch.cli/tests/help_snapshot.rs— snapshot--helpoutput so accidental changes show up in review.cli/tests/env.rs—ZSTD_CLEVEL/ZSTD_NBTHREADSexercised.Out of scope
-T#and forwards viaZSTD_c_nbWorkers; if 6.4 is not merged yet,-T0/-T1work via single-threaded fallback.-don v0.1-v0.7 archives) → 6.5/6.6 (perf(bench): add rust/ffi delta benchmark artifacts #65/perf(decoding): SIMD HUF decode kernels with runtime CPU dispatch #66).--format=gzip|xz|lz4cross-format CLI → deferred (Alpine packagers can disable).Acceptance criteria
zstd FILEcompresses toFILE.zst, removes source on--rm, default compression level matches upstream (= 3).zstd -d FILE.zstdecompresses toFILE.cat FILE | zstd | zstd -d > FILE.out: byte-exact roundtrip via stdin/stdout.zstd -t FILE.zst: silent success, non-zero exit on corruption.zstd -l FILE.zst: emits frame metadata table matching upstream column layout for at least the fixed columns (frame ID, content size, ratio, check, filename).zstdcat FILE.zst: decompresses to stdout regardless of TTY.--versionfirst line:zstd version 1.5.7 (structured-zstd vX.Y.Z).ZSTD_CLEVEL=11 zstd FILEuses level 11.--ultraunlocks-20..-22; without--ultrathose flags error out.-T0forwardsnbWorkers = max_threads; treated as1if 6.4 not merged.cli/tests/suite: 100% green undercargo nextest.Estimate
~12-14 working days (~2000 LoC for CLI, ~500 LoC for tests, plus help-text snapshotting).
Blocked by
compress/decompressmodes.-D+ stdin/stdout paths.References
programs/zstdcli.cv1.5.7--helpoutput: https://github.com/facebook/zstd/blob/v1.5.7/programs/zstdcli.c