vdev/commands/check/
rust.rs

1use anyhow::{Result, bail};
2use std::{ffi::OsString, fs};
3
4use crate::{
5    app,
6    utils::{command::ChainArgs as _, git, paths},
7};
8
9/// Check the Rust code for errors
10#[derive(clap::Args, Debug)]
11#[command()]
12pub struct Cli {
13    #[arg(long, default_value_t = true)]
14    clippy: bool,
15
16    #[arg(value_name = "FEATURE")]
17    features: Vec<String>,
18
19    #[arg(long)]
20    fix: bool,
21}
22
23#[derive(strum::Display, strum::AsRefStr, Clone, Copy, Debug)]
24#[strum(serialize_all = "lowercase")]
25enum Tool {
26    Clippy,
27    Check,
28}
29
30impl Cli {
31    /// Build the argument vector for cargo invocation.
32    fn build_args(&self, tool: Tool) -> Vec<OsString> {
33        let pre_args = if self.fix {
34            vec!["--fix"]
35        } else {
36            Vec::default()
37        };
38
39        let feature_args = if self.features.is_empty() {
40            vec!["--all-features".to_string()]
41        } else {
42            vec![
43                "--no-default-features".to_string(),
44                "--features".to_string(),
45                self.features.join(",").clone(),
46            ]
47        };
48
49        [tool.as_ref(), "--workspace", "--all-targets"]
50            .chain_args(feature_args)
51            .chain_args(pre_args)
52    }
53
54    pub fn exec(self) -> Result<()> {
55        let lock_file = paths::find_repo_root()?.join("Cargo.lock");
56        let lock_before = fs::read(&lock_file)?;
57
58        let tool = if self.clippy {
59            Tool::Clippy
60        } else {
61            Tool::Check
62        };
63
64        app::exec("cargo", self.build_args(tool), true)?;
65
66        let lock_after = fs::read(&lock_file)?;
67        if lock_before != lock_after {
68            bail!(
69                "Cargo.lock was modified by `cargo {tool}`. Please commit the updated Cargo.lock."
70            );
71        }
72
73        // If --fix was used, check for changes and commit them.
74        if self.fix {
75            let has_changes = !git::get_modified_files()?.is_empty();
76            if has_changes {
77                app::exec("cargo", ["fmt", "--all"], true)?;
78                git::commit("chore(vdev): apply vdev rust check fixes")?;
79            }
80        }
81
82        Ok(())
83    }
84}