use std::ffi::OsString;
use clap::Parser;
mod args;
mod commands;
mod config;
mod config_toml;
mod env;
mod error;
mod explain;
mod fix;
mod install;
mod lint;
mod migrate;
mod output;
mod roots;
mod write;
use args::{Cli, Command, Format};
use env::Env;
pub use error::{CliError, Kind};
pub use lint::lint_to_json;
use output::emit_error;
pub fn run(args: impl IntoIterator<Item = OsString>) -> u8 {
let raw: Vec<OsString> = args.into_iter().collect();
env::enable_timings();
let cli = match Cli::try_parse_from(&raw) {
Ok(cli) => cli,
Err(error) => return usage(&raw, &error),
};
match cli.command {
None => lint::run(&cli.lint, Env::detect()),
Some(Command::Rules(args)) => commands::rules(&args),
Some(Command::Schema) => commands::schema(),
Some(Command::Init(args)) => commands::init(&args),
Some(Command::Config(args)) => config::run(&args),
Some(Command::ExplainUsage(args)) => explain::run(&args),
Some(Command::Migrate(args)) => migrate::run(&args),
}
}
pub fn install_panic_hook(raw: &[OsString]) {
if !wants_json(raw) {
return;
}
let default = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
default(info);
emit_error(
&CliError::new(Kind::Internal, format!("internal error: {info}")),
Format::Json,
);
std::process::exit(2);
}));
}
fn usage(raw: &[OsString], error: &clap::Error) -> u8 {
if !error.use_stderr() {
let _ = error.print();
return 0;
}
if wants_json(raw) {
let message = error.to_string();
emit_error(&CliError::new(Kind::Usage, message.trim_end()), Format::Json);
} else {
let _ = error.print();
}
2
}
fn wants_json(raw: &[OsString]) -> bool {
let args: Vec<&str> = raw.iter().filter_map(|arg| arg.to_str()).collect();
args.contains(&"--format=json") || args.windows(2).any(|pair| pair[0] == "--format" && pair[1] == "json")
}