1use std::ffi::OsString;
8
9use clap::Parser;
10
11mod args;
12mod commands;
13mod config;
14mod config_toml;
15mod env;
16mod error;
17mod explain;
18mod fix;
19mod install;
20mod lint;
21mod migrate;
22mod output;
23mod roots;
24mod write;
25
26use args::{Cli, Command, Format};
27use env::Env;
28pub use error::{CliError, Kind};
29pub use lint::lint_to_json;
30use output::emit_error;
31
32pub fn run(args: impl IntoIterator<Item = OsString>) -> u8 {
35 let raw: Vec<OsString> = args.into_iter().collect();
36 env::enable_timings();
37 let cli = match Cli::try_parse_from(&raw) {
38 Ok(cli) => cli,
39 Err(error) => return usage(&raw, &error),
40 };
41 match cli.command {
42 None => lint::run(&cli.lint, Env::detect()),
43 Some(Command::Rules(args)) => commands::rules(&args),
44 Some(Command::Schema) => commands::schema(),
45 Some(Command::Init(args)) => commands::init(&args),
46 Some(Command::Config(args)) => config::run(&args),
47 Some(Command::ExplainUsage(args)) => explain::run(&args),
48 Some(Command::Migrate(args)) => migrate::run(&args),
49 }
50}
51
52pub fn install_panic_hook(raw: &[OsString]) {
58 if !wants_json(raw) {
59 return;
60 }
61 let default = std::panic::take_hook();
62 std::panic::set_hook(Box::new(move |info| {
63 default(info);
64 emit_error(
65 &CliError::new(Kind::Internal, format!("internal error: {info}")),
66 Format::Json,
67 );
68 std::process::exit(2);
69 }));
70}
71
72fn usage(raw: &[OsString], error: &clap::Error) -> u8 {
75 if !error.use_stderr() {
76 let _ = error.print();
77 return 0;
78 }
79 if wants_json(raw) {
80 let message = error.to_string();
81 emit_error(&CliError::new(Kind::Usage, message.trim_end()), Format::Json);
82 } else {
83 let _ = error.print();
84 }
85 2
86}
87
88fn wants_json(raw: &[OsString]) -> bool {
89 let args: Vec<&str> = raw.iter().filter_map(|arg| arg.to_str()).collect();
90 args.contains(&"--format=json") || args.windows(2).any(|pair| pair[0] == "--format" && pair[1] == "json")
91}