1use anyhow::Ok;
2use strum::IntoEnumIterator;
3
4use crate::{
5 commands::WARN_IGNORED_EXCLUDE_AND_ONLY_ARGS,
6 endgroup, group,
7 prelude::{Context, Environment},
8 utils::{
9 cargo::ensure_cargo_crate_is_installed,
10 process::{run_process, run_process_for_package, run_process_for_workspace},
11 workspace::{WorkspaceMemberType, get_workspace_members},
12 },
13 versions::TYPOS_VERSION,
14};
15
16use super::Target;
17
18#[tracel_xtask_macros::declare_command_args(Target, CheckSubCommand)]
19pub struct CheckCmdArgs {}
20
21pub fn handle_command(args: CheckCmdArgs, _env: Environment, _ctx: Context) -> anyhow::Result<()> {
22 if args.target == Target::Workspace && (!args.exclude.is_empty() || !args.only.is_empty()) {
23 warn!("{WARN_IGNORED_EXCLUDE_AND_ONLY_ARGS}");
24 }
25
26 match args.get_command() {
27 CheckSubCommand::Audit => {
28 let res = run_audit();
29 if res.is_err() && args.ignore_audit {
30 warn!("Ignoring audit error because of '--ignore-audit' flag.");
31 Ok(())
32 } else {
33 res
34 }
35 }
36 CheckSubCommand::Format => run_format(&args.target, &args.exclude, &args.only),
37 CheckSubCommand::Lint => run_lint(
38 &args.target,
39 &args.exclude,
40 &args.only,
41 &args.features,
42 args.no_default_features,
43 ),
44 CheckSubCommand::Typos => {
45 let res = run_typos();
46 if res.is_err() && args.ignore_typos {
47 warn!("Ignoring typos error because of '--ignore-typos' flag.");
48 Ok(())
49 } else {
50 res
51 }
52 }
53 CheckSubCommand::All => CheckSubCommand::iter()
54 .filter(|c| *c != CheckSubCommand::All)
55 .try_for_each(|c| {
56 handle_command(
57 CheckCmdArgs {
58 command: Some(c),
59 target: args.target.clone(),
60 exclude: args.exclude.clone(),
61 only: args.only.clone(),
62 ignore_audit: args.ignore_audit,
63 ignore_typos: args.ignore_typos,
64 features: args.features.clone(),
65 no_default_features: args.no_default_features,
66 },
67 _env.clone(),
68 _ctx.clone(),
69 )
70 }),
71 }
72}
73
74fn run_audit() -> anyhow::Result<()> {
75 group!("Audit Rust Dependencies");
76 ensure_cargo_crate_is_installed("cargo-audit", Some("fix"), None, false)?;
77 run_process(
78 "cargo",
79 &["audit", "-q", "--color", "always"],
80 None,
81 None,
82 "Audit check execution failed",
83 )?;
84 endgroup!();
85 Ok(())
86}
87
88fn run_format(target: &Target, excluded: &[String], only: &[String]) -> anyhow::Result<()> {
89 match target {
90 Target::Workspace => {
91 group!("Format Workspace");
92 run_process_for_workspace(
93 "cargo",
94 &["fmt", "--check"],
95 &[],
96 None,
97 None,
98 "Workspace format failed",
99 None,
100 None,
101 )?;
102 endgroup!();
103 }
104 Target::Crates | Target::Examples => {
105 let members = match target {
106 Target::Crates => get_workspace_members(WorkspaceMemberType::Crate),
107 Target::Examples => get_workspace_members(WorkspaceMemberType::Example),
108 _ => unreachable!(),
109 };
110
111 for member in members {
112 group!("Format: {}", member.name);
113 run_process_for_package(
114 "cargo",
115 &member.name,
116 &["fmt", "--check", "-p", &member.name],
117 excluded,
118 only,
119 &format!("Format check execution failed for {}", &member.name),
120 None,
121 None,
122 )?;
123 endgroup!();
124 }
125 }
126 Target::AllPackages => {
127 Target::iter()
128 .filter(|t| *t != Target::AllPackages && *t != Target::Workspace)
129 .try_for_each(|t| run_format(&t, excluded, only))?;
130 }
131 }
132 Ok(())
133}
134
135fn run_lint(
136 target: &Target,
137 excluded: &[String],
138 only: &[String],
139 features: &[String],
140 no_default_features: bool,
141) -> anyhow::Result<()> {
142 match target {
143 Target::Workspace => {
144 group!("Lint Workspace");
145 let mut cmd_args = vec!["clippy", "--no-deps", "--color=always"];
146
147 if no_default_features {
148 cmd_args.push("--no-default-features");
149 }
150
151 let features_str = features.join(",");
152 if !features.is_empty() {
153 cmd_args.push("--features");
154 cmd_args.push(&features_str);
155 }
156
157 cmd_args.extend(&["--", "--deny", "warnings"]);
158
159 run_process_for_workspace(
160 "cargo",
161 &cmd_args,
162 &[],
163 None,
164 None,
165 "Workspace lint failed",
166 None,
167 None,
168 )?;
169 endgroup!();
170 }
171 Target::Crates | Target::Examples => {
172 let members = match target {
173 Target::Crates => get_workspace_members(WorkspaceMemberType::Crate),
174 Target::Examples => get_workspace_members(WorkspaceMemberType::Example),
175 _ => unreachable!(),
176 };
177
178 for member in members {
179 group!("Lint: {}", member.name);
180 let mut cmd_args =
181 vec!["clippy", "--no-deps", "--color=always", "-p", &member.name];
182
183 if no_default_features {
184 cmd_args.push("--no-default-features");
185 }
186
187 let features_str = features.join(",");
188 if !features.is_empty() {
189 cmd_args.push("--features");
190 cmd_args.push(&features_str);
191 }
192
193 cmd_args.extend(&["--", "--deny", "warnings"]);
194
195 run_process_for_package(
196 "cargo",
197 &member.name,
198 &cmd_args,
199 excluded,
200 only,
201 &format!("Lint fix execution failed for {}", &member.name),
202 None,
203 None,
204 )?;
205 endgroup!();
206 }
207 }
208 Target::AllPackages => {
209 Target::iter()
210 .filter(|t| *t != Target::AllPackages && *t != Target::Workspace)
211 .try_for_each(|t| run_lint(&t, excluded, only, features, no_default_features))?;
212 }
213 }
214 Ok(())
215}
216
217fn run_typos() -> anyhow::Result<()> {
218 if std::env::var("CI").is_err() {
219 ensure_cargo_crate_is_installed("typos-cli", None, Some(TYPOS_VERSION), false)?;
220 }
221 group!("Typos");
222 run_process(
223 "typos",
224 &[],
226 None,
227 None,
228 "Typos check execution failed",
229 )?;
230 endgroup!();
231 Ok(())
232}