use clap::Parser;
use i18n_audit_rust::{AuditOptions, AuditRunner, EnvConfig};
#[derive(Debug, Parser)]
#[command(name = "i18n-audit-rust")]
#[command(about = "Audit translation key usage for Rust codebases")]
struct Cli {
#[arg(long, default_value = ".env")]
env_file: String,
#[arg(long)]
locales: Option<String>,
#[arg(long)]
paths: Option<String>,
#[arg(long)]
exclude: Option<String>,
#[arg(long)]
format: Option<String>,
#[arg(long)]
output: Option<String>,
#[arg(long, default_value_t = false)]
only_missing: bool,
#[arg(long, default_value_t = false)]
only_unused: bool,
#[arg(long, default_value_t = false)]
html: bool,
#[arg(long)]
html_output: Option<String>,
#[arg(long)]
lang_paths: Option<String>,
#[arg(long)]
lang_path: Option<String>,
#[arg(long, default_value_t = false)]
follow_symlinks: bool,
#[arg(long, default_value_t = false)]
fail_on_missing: bool,
#[arg(long, default_value_t = false)]
fail_on_unused: bool,
#[arg(long)]
log_path: Option<String>,
#[arg(long)]
dashboard_url: Option<String>,
}
fn main() -> std::process::ExitCode {
let cli = Cli::parse();
let root_path = std::env::current_dir()
.ok()
.map(|path| path.to_string_lossy().to_string())
.unwrap_or_else(|| ".".to_string());
let env = EnvConfig::load(&root_path, &cli.env_file);
let mut options = AuditOptions::default();
options.root_path = root_path;
options.locales = cli
.locales
.map(split_csv)
.unwrap_or_else(|| env.get_csv_array("I18N_AUDIT_LOCALES", Vec::new()));
options.paths = cli.paths.map(split_csv).unwrap_or_else(|| {
env.get_csv_array(
"I18N_AUDIT_SCAN_PATHS",
vec!["src".to_string(), "examples".to_string(), "tests".to_string()],
)
});
options.exclude = cli.exclude.map(split_csv).unwrap_or_else(|| {
env.get_csv_array(
"I18N_AUDIT_EXCLUDE_PATHS",
vec![
"target".to_string(),
"vendor".to_string(),
"node_modules".to_string(),
".git".to_string(),
],
)
});
options.format = cli
.format
.unwrap_or_else(|| env.get_string("I18N_AUDIT_FORMAT", "table"))
.to_ascii_lowercase();
options.output = cli.output;
options.only_missing = cli.only_missing;
options.only_unused = cli.only_unused;
options.html = cli.html;
options.html_output = cli
.html_output
.unwrap_or_else(|| env.get_string("I18N_AUDIT_HTML_OUTPUT_PATH", "target/i18n-audit-latest.html"));
options.lang_paths = if let Some(paths) = cli.lang_paths {
split_csv(paths)
} else if let Some(path) = cli.lang_path {
split_csv(path)
} else {
env.get_csv_array("I18N_AUDIT_LANG_PATHS", vec!["locales".to_string()])
};
options.follow_symlinks = cli.follow_symlinks || env.get_bool("I18N_AUDIT_FOLLOW_SYMLINKS", false);
options.fail_on_missing = cli.fail_on_missing;
options.fail_on_unused = cli.fail_on_unused;
options.log_path = cli
.log_path
.unwrap_or_else(|| env.get_string("I18N_AUDIT_LOG_PATH", "target/i18n-audit.log"));
options.dashboard_url = cli
.dashboard_url
.unwrap_or_else(|| env.get_string("I18N_AUDIT_DASHBOARD_URL", ""));
let runner = AuditRunner::default();
let outcome = match runner.run(&options) {
Ok(outcome) => outcome,
Err(error) => {
eprintln!("i18n-audit-rust failed: {error}");
return std::process::ExitCode::from(1);
}
};
if options.format == "json" {
println!("{}", outcome.json_output);
} else {
print!("{}", outcome.table_output);
}
if options.fail_on_missing && outcome.has_missing {
return std::process::ExitCode::from(1);
}
if options.fail_on_unused && outcome.has_unused {
return std::process::ExitCode::from(1);
}
std::process::ExitCode::SUCCESS
}
fn split_csv(value: String) -> Vec<String> {
value
.split(',')
.map(|item| item.trim().to_string())
.filter(|item| !item.is_empty())
.collect()
}