feat: CLI-Verbesserungen nach detox-Analyse

- --dry-run als primäre Option (--no-changes als deprecated alias)
- --special für Symlinks und Special Files
- Smart Default-Excludes: .git, .svn, node_modules, .cache, __pycache__
  werden automatisch ignoriert (ähnlich wie detox)
- Alle Änderungen rückwärtskompatibel

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-10 10:11:59 +01:00
commit dbb3a092a6
2 changed files with 40 additions and 8 deletions

View file

@ -6,7 +6,7 @@ use std::path::PathBuf;
#[clap(about, version, author)]
#[clap(group(
ArgGroup::new("mode")
.args(&["no_changes", "force"])
.args(&["dry_run", "force"])
.multiple(false)
))]
pub struct Cli {
@ -17,9 +17,9 @@ pub struct Cli {
#[clap(short, long)]
pub quiet: bool,
/// Nur anzeigen, aber keine realen Änderungen vornehmen
#[clap(short, long)]
pub no_changes: bool,
/// Nur anzeigen, aber keine realen Änderungen vornehmen (dry-run)
#[clap(short = 'n', long = "dry-run", alias = "no-changes")]
pub dry_run: bool,
/// Existierende Dateien überschreiben
#[clap(short, long)]
@ -36,4 +36,8 @@ pub struct Cli {
/// Erlaubt, auch das Wurzelverzeichnis anzupassen
#[clap(long)]
pub modify_root: bool,
/// Auch symbolische Links und Special Files verarbeiten
#[clap(long)]
pub special: bool,
}

View file

@ -14,6 +14,20 @@ use sanitizer::{clean_filename, is_excluded, is_safe_rename};
use std::fs;
use walkdir::WalkDir;
// Standard-Ausschlussmuster (ähnlich wie detox)
const DEFAULT_EXCLUDES: &[&str] = &[
".git",
".git/**",
".svn",
".svn/**",
"node_modules",
"node_modules/**",
".cache",
".cache/**",
"__pycache__",
"__pycache__/**",
];
/// Startpunkt des Programms
fn main() -> Result<()> {
// Initialisiere Logger
@ -26,9 +40,14 @@ fn main() -> Result<()> {
let config = Config::from_default_locations(args.verbose)?;
// let config = Config::load(".NameToUnix.conf", args.verbose)?;
// Ausschlussmuster (Glob-Patterns) vorbereiten
let exclude_patterns = args
.exclude
// Ausschlussmuster (Glob-Patterns) vorbereiten - Default-Excludes + User-Excludes
let mut all_excludes = DEFAULT_EXCLUDES
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
all_excludes.extend(args.exclude.clone());
let exclude_patterns = all_excludes
.iter()
.map(|pattern| {
Pattern::new(pattern)
@ -89,6 +108,15 @@ fn main() -> Result<()> {
continue;
}
// Special Files (Symlinks, etc.) nur mit --special verarbeiten
let file_type = entry.file_type();
if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
if args.verbose {
debug!("Skip special file: {}", old_path.display());
}
continue;
}
// Dateiname (oder Verzeichnisname) ermitteln
let filename = old_path.file_name().ok_or_else(|| {
anyhow::anyhow!(
@ -105,7 +133,7 @@ fn main() -> Result<()> {
info!("{} -> {}", old_path.display(), new_path.display());
}
if !args.no_changes && is_safe_rename(old_path, &new_path, args.force) {
if !args.dry_run && is_safe_rename(old_path, &new_path, args.force) {
fs::rename(old_path, &new_path).with_context(|| {
format!(
"Fehler beim Umbenennen: {} -> {}",