use clap::Parser;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
#[derive(clap::Parser)]
#[expect(
clippy::struct_excessive_bools,
reason = "the bools are clap CLI flags, not an accumulating design"
)]
#[command(
name = "dejadoc",
bin_name = "cargo dejadoc",
version,
about = "Find duplicated Rust doctests and functions across a workspace"
)]
struct Args {
#[arg(short, long)]
package: Option<String>,
#[arg(long)]
all_targets: bool,
#[arg(long)]
json: bool,
#[arg(short = 't', long, value_name = "N")]
threshold: Option<usize>,
#[arg(long, value_name = "N")]
min_tokens: Option<usize>,
#[arg(long)]
no_functions: bool,
#[arg(long, value_name = "N")]
fn_min_tokens: Option<usize>,
#[arg(long, value_name = "PATH")]
config: Option<PathBuf>,
#[arg(long)]
no_fail: bool,
#[arg(short, long)]
verbose: bool,
#[arg(long)]
github: bool,
}
fn main() -> ExitCode {
let mut args: Vec<String> = std::env::args().collect();
if args.get(1).is_some_and(|a| a == "dejadoc") {
args.remove(1);
}
match run(&Args::parse_from(args)) {
Ok(code) => code,
Err(err) => {
eprintln!("dejadoc: {err}");
ExitCode::from(2)
}
}
}
fn run(args: &Args) -> dejadoc::Result<ExitCode> {
let mut scan = dejadoc::Dejadoc::default();
if let Some(package) = &args.package {
scan = scan.package(package);
}
if args.all_targets {
scan = scan.all_targets();
}
if let Some(threshold) = args.threshold {
scan = scan.threshold(threshold);
}
if let Some(min_tokens) = args.min_tokens {
scan = scan.min_tokens(min_tokens);
}
if args.no_functions {
scan = scan.no_functions();
}
if let Some(n) = args.fn_min_tokens {
scan = scan.fn_min_tokens(n);
}
if let Some(config) = &args.config {
scan = scan.config(config)?;
}
let report = scan.run(Path::new("."))?;
let out = if args.json {
dejadoc::json(&report)
} else {
let mut out = dejadoc::human(&report, args.verbose);
if args.github {
out.push('\n');
out.push_str(&dejadoc::annotations(&report, args.no_fail));
}
out
};
println!("{out}");
Ok(dejadoc::exit_code(&report, args.no_fail))
}