use std::path::PathBuf;
use crate::cli;
use crate::common::config::Config;
use crate::core::{report, sorter};
use crate::rules::rules_file::RulesFile;
use anyhow::Result;
use clap::Args;
use colored::Colorize;
use indicatif::ProgressBar;
#[derive(Args)]
#[command(about = "🚀 Sort files in the source folder using defined rules")]
pub struct SortArgs {
#[arg(long, help = "Override the default source folder path")]
pub source: Option<String>,
#[arg(
long,
help = "Comma-separated list of rule IDs to execute (use '<all>' for all rules)"
)]
pub rules: Option<String>,
#[arg(
long,
help = "Generate a report in the specified format (pdf, csv, json)"
)]
pub report: Option<String>,
#[arg(long, help = "Directory where the report will be saved")]
pub output: Option<String>,
#[arg(
long,
default_value_t = false,
help = "Preview what would happen without actually moving files"
)]
pub dry_run: bool,
}
pub fn run(args: SortArgs) -> Result<()> {
if args.dry_run {
cli::warning("🔍 Running in dry-run mode - no files will be moved");
} else {
cli::info("🚀 Starting file sorting...");
}
log::info!(
"Running sort with source: {:?}, rules: {:?}, dry_run: {}",
args.source,
args.rules,
args.dry_run
);
let config = Config::load()?;
let source_path = if let Some(source) = args.source {
if source == "<default>" {
config.source_folder.clone()
} else {
PathBuf::from(source)
}
} else {
config.source_folder.clone()
};
let rules_file = RulesFile::load()?;
let rule_filter = args.rules.as_ref().and_then(|r| {
if r == "<all>" {
None
} else {
Some(
r.split(',')
.map(|s| s.trim().to_string())
.collect::<Vec<_>>(),
)
}
});
let optimized_rules = rules_file.optimized_with_filter(rule_filter.as_deref())?;
let files = sorter::collect_files(&source_path)?;
let pb = ProgressBar::new(files.len() as u64);
pb.set_style(cli::progress_style());
let results = sorter::sort_files(
&files,
&source_path,
&optimized_rules,
args.dry_run,
Some(|| {
pb.inc(1);
}),
)?;
pb.finish_with_message("✅ Sorting complete");
cli::success("Sorting completed successfully!");
log::info!("Sorting completed, found {} matches", results.len());
if args.report.is_none() && !results.is_empty() {
cli::header("📁 Sorted Files");
println!(
"{} | {} | {} | {}",
"File".bright_cyan().bold(),
"Matched Rule".bright_cyan().bold(),
"Current Path".bright_cyan().bold(),
"New Path".bright_cyan().bold()
);
println!("{}", "─".repeat(120).bright_black());
for result in &results {
println!(
"{:<40} | {:<30} | {:<40} | {}",
result.file_name.bright_white(),
result.matched_rule_id.green(),
result.current_path.display().to_string().yellow(),
result.new_path.display().to_string().blue()
);
}
} else if results.is_empty() {
cli::info("No files matched the sorting rules.");
}
if let Some(report_type) = &args.report {
log::info!("Generating report of type: {report_type}");
let output_dir = args.output.as_ref().map_or_else(
|| {
std::env::current_dir().unwrap_or_else(|_| {
cli::warning("Cannot get current working directory, using '.'");
PathBuf::from(".")
})
},
PathBuf::from,
);
report::generate_report(report_type, &output_dir, &results)?;
cli::success(&format!(
"Report generated successfully in {}",
output_dir.display()
));
}
Ok(())
}