use std::path::PathBuf;
use std::process;
use clap::Parser;
use dupes_core::cli::{self, CliOverrides, Command, OutputFormat};
use dupes_rust::RustAnalyzer;
#[derive(Parser)]
#[command(
name = "cargo-dupes",
version,
about = "Detect duplicate code in Rust codebases"
)]
struct Cli {
#[arg(hide = true, default_value = "")]
_cargo_subcommand: String,
#[command(subcommand)]
command: Option<Command>,
#[arg(short, long, global = true)]
path: Option<PathBuf>,
#[arg(long, global = true)]
min_nodes: Option<usize>,
#[arg(long, global = true)]
min_lines: Option<usize>,
#[arg(long, global = true)]
threshold: Option<f64>,
#[arg(long, global = true, default_value = "text")]
format: OutputFormat,
#[arg(long, global = true)]
exclude: Vec<String>,
#[arg(long, global = true)]
exclude_tests: bool,
#[arg(long, short = 's', global = true)]
sub_function: bool,
#[arg(long, global = true)]
min_sub_nodes: Option<usize>,
}
fn main() {
let Cli {
command,
path,
min_nodes,
min_lines,
threshold,
format,
exclude,
exclude_tests,
sub_function,
min_sub_nodes,
..
} = Cli::parse();
let root =
path.unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
let command = command.unwrap_or(Command::Report);
let stdout = std::io::stdout();
let mut writer = stdout.lock();
let result = match &command {
Command::Ignore {
fingerprint,
reason,
} => cli::cmd_ignore(&root, fingerprint, reason.clone(), &mut writer),
Command::Ignored => cli::cmd_ignored(&root, &mut writer),
_ => {
let analyzer = RustAnalyzer::new();
let overrides = CliOverrides {
min_nodes,
min_lines,
threshold,
exclude,
exclude_tests: if exclude_tests { Some(true) } else { None },
sub_function: if sub_function { Some(true) } else { None },
min_sub_nodes,
};
let output = match cli::run_analysis(&analyzer, &root, format, &overrides) {
Ok(o) => o,
Err(e) => {
eprintln!("Error: {e}");
process::exit(e.exit_code());
}
};
for warning in &output.result.warnings {
eprintln!("Warning: {warning}");
}
let reporter: &dyn dupes_core::output::Reporter = &*output.reporter;
match &command {
Command::Stats => cli::cmd_stats(&output.result, reporter, &mut writer),
Command::Report => cli::cmd_report(&output.result, reporter, &mut writer),
Command::Check {
max_exact,
max_near,
max_exact_percent,
max_near_percent,
} => cli::cmd_check(
&output.config,
&output.result,
reporter,
&mut writer,
&cli::CheckThresholds {
max_exact: *max_exact,
max_near: *max_near,
max_exact_percent: *max_exact_percent,
max_near_percent: *max_near_percent,
},
),
Command::Cleanup { dry_run } => {
cli::cmd_cleanup(&root, &output.result, &mut writer, *dry_run)
}
Command::Ignore { .. } | Command::Ignored => unreachable!(),
}
}
};
if let Err(e) = result {
if matches!(e, cli::CliError::CheckFailed) {
process::exit(1);
} else {
eprintln!("Error: {e}");
process::exit(e.exit_code());
}
}
}