main.rs

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