feat: Parallele Verarbeitung mit rayon bei vielen Dateien
- Bei >= 100 Dateien wird rayon für parallele Berechnung genutzt - clean_filename() wird parallel ausgeführt (CPU-intensive Operation) - Tatsächliche Umbenennung bleibt sequenziell (I/O-bound, Race-Conditions vermeiden) - Bei < 100 Dateien sequenziell (Overhead vermeiden) - RenameOperation-Struct für geplante Umbenennungen Performance-Gewinn bei großen Verzeichnisbäumen mit vielen Dateien. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e97025e17c
commit
ca49404dfa
1 changed files with 84 additions and 42 deletions
126
src/main.rs
126
src/main.rs
|
|
@ -10,8 +10,10 @@ use config::Config;
|
|||
use glob::Pattern;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use log::{debug, error, info};
|
||||
use rayon::prelude::*;
|
||||
use sanitizer::{clean_filename, is_excluded, is_safe_rename};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
// Standard-Ausschlussmuster (ähnlich wie detox)
|
||||
|
|
@ -28,6 +30,16 @@ const DEFAULT_EXCLUDES: &[&str] = &[
|
|||
"__pycache__/**",
|
||||
];
|
||||
|
||||
// Schwellwert für parallele Verarbeitung (bei weniger Dateien lohnt sich Overhead nicht)
|
||||
const PARALLEL_THRESHOLD: usize = 100;
|
||||
|
||||
/// Repräsentiert eine geplante Umbenennungsoperation
|
||||
#[derive(Debug)]
|
||||
struct RenameOperation {
|
||||
old_path: PathBuf,
|
||||
new_path: PathBuf,
|
||||
}
|
||||
|
||||
/// Startpunkt des Programms
|
||||
fn main() -> Result<()> {
|
||||
// Initialisiere Logger
|
||||
|
|
@ -92,56 +104,86 @@ fn main() -> Result<()> {
|
|||
None
|
||||
};
|
||||
|
||||
// Umbenennen in absteigender Tiefe
|
||||
for entry in entries {
|
||||
// Berechne Umbenennungen (parallel bei vielen Dateien)
|
||||
let rename_ops: Vec<RenameOperation> = if entries.len() >= PARALLEL_THRESHOLD {
|
||||
// Parallel mit rayon
|
||||
entries
|
||||
.par_iter()
|
||||
.filter_map(|entry| {
|
||||
let old_path = entry.path();
|
||||
|
||||
// Ebenentiefe 0 -> überspringen
|
||||
if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Special Files nur mit --special
|
||||
let file_type = entry.file_type();
|
||||
if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Dateiname ermitteln und bereinigen
|
||||
let filename = old_path.file_name()?;
|
||||
let new_name = clean_filename(filename, &config, false)?;
|
||||
let new_path = old_path.with_file_name(&new_name);
|
||||
|
||||
Some(RenameOperation {
|
||||
old_path: old_path.to_path_buf(),
|
||||
new_path,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
// Sequenziell bei wenigen Dateien
|
||||
entries
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
let old_path = entry.path();
|
||||
|
||||
if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
|
||||
return None;
|
||||
}
|
||||
|
||||
let file_type = entry.file_type();
|
||||
if !args.special && (!file_type.is_file() && !file_type.is_dir()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let filename = old_path.file_name()?;
|
||||
let new_name = clean_filename(filename, &config, false)?;
|
||||
let new_path = old_path.with_file_name(&new_name);
|
||||
|
||||
Some(RenameOperation {
|
||||
old_path: old_path.to_path_buf(),
|
||||
new_path,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Umbenennungen sequenziell ausführen
|
||||
for op in rename_ops {
|
||||
if let Some(bar) = &progress_bar {
|
||||
bar.inc(1);
|
||||
}
|
||||
|
||||
let old_path = entry.path();
|
||||
|
||||
// Ebenentiefe 0 -> überspringen wir als Verzeichnis, außer --modify_root ist gesetzt
|
||||
if entry.depth() == 0 && entry.file_type().is_dir() && !args.modify_root {
|
||||
if args.verbose {
|
||||
debug!("Skip root directory: {}", old_path.display());
|
||||
}
|
||||
continue;
|
||||
if !args.quiet {
|
||||
info!("{} -> {}", op.old_path.display(), op.new_path.display());
|
||||
}
|
||||
|
||||
// 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;
|
||||
if args.verbose {
|
||||
debug!("Rename: {:?} -> {:?}", op.old_path, op.new_path);
|
||||
}
|
||||
|
||||
// Dateiname (oder Verzeichnisname) ermitteln
|
||||
let filename = old_path.file_name().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Konnte Dateinamen nicht ermitteln für: {}",
|
||||
old_path.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
// Verarbeiten und ggf. umbenennen
|
||||
if let Some(new_name) = clean_filename(filename, &config, args.verbose) {
|
||||
let new_path = old_path.with_file_name(&new_name);
|
||||
|
||||
if !args.quiet {
|
||||
info!("{} -> {}", old_path.display(), new_path.display());
|
||||
}
|
||||
|
||||
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: {} -> {}",
|
||||
old_path.display(),
|
||||
new_path.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
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()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue