release: Version 0.2.0 mit Statistiken und CHANGELOG

- Version erhöht von 0.1.0 auf 0.2.0
- CHANGELOG.md hinzugefügt mit vollständiger Feature-Liste
- Statistiken am Ende: Verarbeitete/geplante/umbenannte Dateien
- Fehler-Zähler für übersprungene/fehlgeschlagene Umbenennungen
- Kommentar-Klarstellung in sanitizer.rs
- Bessere Fehlerbehandlung mit detaillierten Error-Messages

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-10 10:30:18 +01:00
commit f4006ba99d
6 changed files with 88 additions and 15 deletions

42
CHANGELOG.md Normal file
View file

@ -0,0 +1,42 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.2.0] - 2025-02-10
### Added
- **CLI**: `--dry-run` as primary option (with `--no-changes` as deprecated alias for backward compatibility)
- **CLI**: `--special` flag to process symlinks and special files (normally skipped)
- **Smart Default Excludes**: Automatically ignore `.git`, `.svn`, `node_modules`, `.cache`, `__pycache__`
- **Double Extensions**: Proper handling of `.tar.gz`, `.tar.bz2`, `.tar.xz`, `.tar.zst`, `.tar.lz`, `.tar.Z`
- **Parallel Processing**: Using `rayon` for parallel filename cleaning when processing ≥100 files
- **Write Permission Checks**: Check write permissions before attempting rename operations
- **Unit Tests**: 9 comprehensive tests for `clean_filename()` covering edge cases
### Fixed
- **Critical Bug**: Hidden files (like `.gitignore`) are no longer incorrectly renamed to `unnamed.xxx`
- Leading dot in hidden files is now correctly preserved
- Fixed all clippy warnings
### Changed
- Binary renamed from `NameToUnix` to `ntu` (shorter CLI usage)
- Improved error messages for permission issues
- Better handling of hidden files with spaces (`.my config``.my_config`)
### Performance
- Parallel processing with rayon for large directory trees (threshold: 100 files)
- Optimized regex patterns using `once_cell::Lazy`
## [0.1.0] - 2025-03-07
### Added
- Initial release
- Basic filename sanitization
- Configurable replacements via TOML
- Recursive directory processing
- Exclude patterns support
- German umlaut conversion
- Special identifier preservation (C++, C#)

4
Cargo.lock generated
View file

@ -1,10 +1,10 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "NameToUnix"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"anyhow",
"assert_fs",

View file

@ -1,6 +1,6 @@
[package]
name = "NameToUnix"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
authors = ["Dieter Schlüter <dieter.schlueter@linix.de>"]
description = "Ein Tool zum Anpassen von Verzeichnis- und Dateinamen an Linux-Konventionen"

View file

@ -162,6 +162,12 @@ fn main() -> Result<()> {
.collect()
};
// Statistiken
let total_processed = entries.len();
let total_planned = rename_ops.len();
let mut renamed_count = 0;
let mut skipped_count = 0;
// Umbenennungen sequenziell ausführen
for op in rename_ops {
if let Some(bar) = &progress_bar {
@ -176,19 +182,44 @@ fn main() -> Result<()> {
debug!("Rename: {:?} -> {:?}", op.old_path, op.new_path);
}
if !args.dry_run && is_safe_rename(&op.old_path, &op.new_path, args.force) {
fs::rename(&op.old_path, &op.new_path).with_context(|| {
format!(
"Fehler beim Umbenennen: {} -> {}",
op.old_path.display(),
op.new_path.display()
)
})?;
if !args.dry_run {
if is_safe_rename(&op.old_path, &op.new_path, args.force) {
match fs::rename(&op.old_path, &op.new_path) {
Ok(_) => renamed_count += 1,
Err(e) => {
error!(
"Fehler beim Umbenennen: {} -> {}: {}",
op.old_path.display(),
op.new_path.display(),
e
);
skipped_count += 1;
}
}
} else {
skipped_count += 1;
}
}
}
if let Some(bar) = &progress_bar {
bar.finish_with_message("Umbenennung abgeschlossen");
bar.finish_with_message("Verarbeitung abgeschlossen");
}
// Zusammenfassung ausgeben (außer im quiet mode)
if !args.quiet {
info!("");
info!("=== Zusammenfassung für {} ===", path.display());
info!("Verarbeitete Dateien/Verzeichnisse: {}", total_processed);
info!("Umbenennungen geplant: {}", total_planned);
if args.dry_run {
info!("Modus: Dry-run (keine Änderungen)");
} else {
info!("Erfolgreich umbenannt: {}", renamed_count);
if skipped_count > 0 {
info!("Übersprungen/Fehler: {}", skipped_count);
}
}
}
}

View file

@ -62,12 +62,12 @@ pub fn clean_filename(name: &OsStr, config: &Config, verbose: bool) -> Option<St
base = preserve_special_identifiers(&base);
ext = preserve_special_identifiers(&ext);
// 1) Konfig-Replacements zuerst
// 1) Konfig-Replacements anwenden (zuerst)
for (k, v) in &config.replacements {
base = base.replace(k, v);
}
// 2) Dann erst hart-codierte Ersetzungen anwenden
// 2) Danach hart-codierte Ersetzungen anwenden
base = apply_hardcoded_replacements(&base);
// 3) Emojis und hochgestellte Zeichen ersetzen

View file

@ -1 +1 @@
{"rustc_fingerprint":16656946018727591243,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.82.0 (f6e511eec 2024-10-15)\nbinary: rustc\ncommit-hash: f6e511eec7342f59a25f7c0534f1dbea00d01b14\ncommit-date: 2024-10-15\nhost: x86_64-unknown-linux-gnu\nrelease: 1.82.0\nLLVM version: 19.1.1\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/dschlueter/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
{"rustc_fingerprint":18315677830234863098,"outputs":{"8185672236408668984":{"success":true,"status":"","code":0,"stdout":"rustc 1.90.0 (1159e78c4 2025-09-14)\nbinary: rustc\ncommit-hash: 1159e78c4747b02ef996e55082b704c09b970588\ncommit-date: 2025-09-14\nhost: x86_64-unknown-linux-gnu\nrelease: 1.90.0\nLLVM version: 20.1.8\n","stderr":""},"11742744481059712885":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/dschlueter/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}