diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d97d080 --- /dev/null +++ b/CHANGELOG.md @@ -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#) diff --git a/Cargo.lock b/Cargo.lock index 84a1b36..c0dc258 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 74dac01..a491460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "NameToUnix" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Dieter Schlüter "] description = "Ein Tool zum Anpassen von Verzeichnis- und Dateinamen an Linux-Konventionen" diff --git a/src/main.rs b/src/main.rs index ee9be11..443cce8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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); + } + } } } diff --git a/src/sanitizer.rs b/src/sanitizer.rs index c81c236..34734e6 100644 --- a/src/sanitizer.rs +++ b/src/sanitizer.rs @@ -62,12 +62,12 @@ pub fn clean_filename(name: &OsStr, config: &Config, verbose: bool) -> Option