1#![allow(clippy::disallowed_methods, reason = "tooling is exempt")]
 2use std::process::Command;
 3
 4use anyhow::{Context as _, Result, bail};
 5use clap::Parser;
 6
 7#[derive(Parser)]
 8pub struct ClippyArgs {
 9    /// Automatically apply lint suggestions (`clippy --fix`).
10    #[arg(long)]
11    fix: bool,
12
13    /// The package to run Clippy against (`cargo -p <PACKAGE> clippy`).
14    #[arg(long, short)]
15    package: Option<String>,
16}
17
18pub fn run_clippy(args: ClippyArgs) -> Result<()> {
19    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
20
21    let mut clippy_command = Command::new(&cargo);
22    clippy_command.arg("clippy");
23
24    if let Some(package) = args.package.as_ref() {
25        clippy_command.args(["--package", package]);
26    } else {
27        clippy_command.arg("--workspace");
28    }
29
30    clippy_command
31        .arg("--release")
32        .arg("--all-targets")
33        .arg("--all-features");
34
35    if args.fix {
36        clippy_command.arg("--fix");
37    }
38
39    clippy_command.arg("--");
40
41    // Deny all warnings.
42    clippy_command.args(["--deny", "warnings"]);
43
44    eprintln!(
45        "running: {cargo} {}",
46        clippy_command
47            .get_args()
48            .map(|arg| arg.to_str().unwrap())
49            .collect::<Vec<_>>()
50            .join(" ")
51    );
52
53    let exit_status = clippy_command
54        .spawn()
55        .context("failed to spawn child process")?
56        .wait()
57        .context("failed to wait for child process")?;
58
59    if !exit_status.success() {
60        bail!("clippy failed: {}", exit_status);
61    }
62
63    Ok(())
64}