rusty-detox 0.1.0

Sanitize messy filenames — a Rust port of Doug Harple's `detox(1)` with the filter pipeline (uncgi, iso8859_1, utf_8, safe, wipeup, max_length), `detoxrc` config grammar, recursive collision-safe batch rename, EXDEV cross-device fallback, and a typed library API.
Documentation

rusty-detox

crates.io docs.rs license: MIT OR Apache-2.0

Sanitize messy filenames. A Rust port of Doug Harple's detox(1) with the full upstream filter pipeline, detoxrc config grammar, recursive collision-safe batch rename, EXDEV cross-device fallback, and a typed library API.

Part of the Rusty portfolio.

Install

cargo install rusty-detox
# or, with prebuilt binaries:
cargo binstall rusty-detox

Usage

# Preview a rename (dry-run)
rusty-detox -n 'My Résumé (final v2).pdf'
# My Résumé (final v2).pdf -> My_Resume_final_v2.pdf

# Recursive batch-rename a directory tree
rusty-detox -r ./downloads/

# Pick a sequence
rusty-detox -s utf_8 ./*.pdf

# List loaded sequences
rusty-detox -L

Cargo Features

Feature Default What it gates
cli yes clap + clap_complete + anyhow + terminal_size + walkdir
inline-detox no Upstream-canonical inline-detox companion binary

Library consumers can use rusty-detox = { version = "0.1", default-features = false } to get the filter pipeline + library API without any CLI dependencies.

Library API

use rusty_detox::{Detox, DetoxBuilder, Sequence};

let detox = DetoxBuilder::new()
    .sequence(Sequence::utf_8())
    .build();
let cleaned = detox.sanitize("My Résumé (final v2).pdf");
// cleaned == "My_Resume_final_v2.pdf"

Compatibility

rusty-detox has two modes:

  • Default — clap-styled flag parser; rejects conflicting flag pairs at parse time; adds --help, --version, completions subcommand.
  • Strict (--strict, env RUSTY_DETOX_STRICT=1, or argv[0] = detox/detox-alias) — byte-equal stderr against upstream v3.0.1 for documented diagnostics; last-wins flag resolution; no subcommands.

The upstream Table.utf_8 and 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 and target briefly. Upstream detox(1) has the same property. Run rusty-detox to completion on a quiescent tree if external observers are critical.

MSRV

Rust 1.85 (edition 2024). Re-verified against stable-minus-two policy at each release.

License

Dual-licensed under MIT or Apache-2.0.