rusty-detox
Sanitize messy filenames through a configurable filter pipeline. Rust port of Doug Harple's detox(1) 3.0.1.
Ships the full upstream filter pipeline, detoxrc config grammar, recursive collision-safe batch rename, EXDEV cross-device fallback, & a typed library API. Default mode adds parse-time conflict rejection, --help/--version, & a completions subcommand. Strict mode reverts every observable surface to byte-equal detox 3.0.1 for drop-in migration.
Part of the Rusty portfolio.
Install
# or, with prebuilt binaries:
# or, download directly from GitHub Releases:
# https://github.com/jsh562/rusty-detox/releases
To also install the inline-detox companion binary that reads stdin to EOF & sanitizes the bytes as a single name:
Usage
# Preview a rename without touching the file
# → My Résumé (final v2).pdf -> My_Resume_final_v2.pdf
# Recursively clean a download tree (collision-safe)
# Pick a specific sanitizer sequence
# List loaded sequences so you can pick one
# Sanitize a stream as a single name (e.g. inside a shell pipeline)
|
# Strict detox-compat mode (drop-in detox 3.0.1 replacement)
RUSTY_DETOX_STRICT=1
# Shell completions
Library API
The library exposes the filter pipeline, Sequence builder, Detox / DetoxBuilder API, & EXDEV-aware renamer without any CLI deps. Use it when you want detox's sanitizer behavior inside another tool.
use ;
let detox = new
.sequence
.build;
let cleaned = detox.sanitize;
// cleaned == "My_Resume_final_v2.pdf"
For library-only consumers without CLI deps see the Cargo Features section.
Cargo Features
default enables full, which (for this single-capability port) resolves to the cli umbrella. detox-classic reproduces v0.1.x bare-port behavior matching upstream detox 3.0.1 1:1. To strip the CLI surface use default-features = false or --no-default-features & add the features you want.
rusty-detox is a single-capability port: its one documented job is "sanitize messy filenames through a configurable filter pipeline". No optional feature leaves are carved beyond the required umbrellas; see docs/feature-layout.md for why.
Feature matrix
| Feature | Description | Umbrella(s) |
|---|---|---|
cli |
All CLI-only dependencies (clap, clap_complete, anyhow, terminal_size, walkdir) and the binary entry point, mode resolver, output formatter, recursive walker, and Strict-mode pre-scanner. Library consumers strip via default-features = false. |
full, detox-classic, detox-minimal, inline-detox |
inline-detox |
Installs an additional inline-detox binary alongside rusty-detox. Reads stdin to EOF, sanitizes the byte stream as a single name, writes to stdout (the upstream-canonical companion entry). |
(standalone, implies cli) |
Preset bundles
| Bundle | Composition | Use case |
|---|---|---|
detox-classic |
cli |
Drop-in upstream detox 3.0.1 replacement. Strict mode is invoked via --strict, RUSTY_DETOX_STRICT, or detox/detox-alias argv[0] auto-detect. |
detox-minimal |
cli |
Explicit minimal-CLI alias for users who prefer the <port>-minimal naming convention seen across other portfolio ports. Identical composition to detox-classic. |
Keep-list workaround (Cargo features are union-only)
Cargo features cannot subtract from default. To get "everything except a specific feature," disable defaults & enumerate the features you want:
# → bare CLI with no inline-detox companion.
# → CLI + the inline-detox companion binary.
For the common cases the named preset bundles are usually sufficient.
Library-only consumers
[]
= { = "0.2", = false }
This strips clap, clap_complete, anyhow, terminal_size, & walkdir. The resulting build pulls only thiserror (required by the always-on DetoxError enum).
Convention authority
This layout follows the portfolio-wide Cargo Features Convention. The "why" lives in ADR-0006; the "what" lives in project-instructions.md §Cargo Feature Surface. Every Rusty port from v0.2 onward exposes the same umbrella set (default / full / cli / <port>-classic), per-port leaves named in kebab-case, & 2 to 4 preset bundles.
Compatibility
rusty-detox has two modes:
- Default mode. clap-styled flag parser. Conflicting flag pairs MUST be rejected at parse time.
--help,--version, & thecompletionssubcommand are all available. - Strict mode (activated by
--strict,RUSTY_DETOX_STRICT=1, or invoking the binary asdetox/detox-alias). Byte-equal stderr against upstream v3.0.1 for documented diagnostics. Last-wins flag resolution.--help,--version, &completionsMUST be rejected.
The upstream Table.utf_8 & Table.iso8859_1 are vendored at v3.0.1 freeze. Future re-vendoring is a MAJOR semver bump.
Concurrency
The EXDEV cross-device rename fallback (copy + fsync + rename + unlink) is not atomic across the full chain. A concurrent reader could observe both the source & target briefly. Upstream detox(1) has the same property. Run rusty-detox to completion on a quiescent tree if external observers are critical.
See docs/COMPATIBILITY.md for the full per-flag matrix.
What's not shipped
- Atomic cross-device rename. The EXDEV fallback (copy + fsync + rename + unlink) MUST NOT be relied on for atomicity across the full chain. Upstream
detox(1)has the same property. - Re-vendoring of
Table.utf_8/Table.iso8859_1mid-version. Both tables are vendored at v3.0.1 freeze; any change is a MAJOR semver bump.
MSRV
Rust 1.85 (edition 2024). Re-verified against the portfolio's stable-minus-two policy at each release.
License
Dual-licensed under MIT or Apache-2.0 at your option.