1use crate::error::{ValidationError, ValidationResult};
4use crate::provider::{OptionDef, OptionProvider, OptionValueType, ValueValidator};
5use std::collections::HashMap;
6
7pub struct CargoProvider {
9 options: HashMap<String, Vec<OptionDef>>,
10}
11
12impl Default for CargoProvider {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl CargoProvider {
19 pub fn new() -> Self {
20 Self {
21 options: build_cargo_options(),
22 }
23 }
24}
25
26impl OptionProvider for CargoProvider {
27 fn name(&self) -> &str {
28 "cargo"
29 }
30
31 fn get_options(&self, command: &str) -> Vec<OptionDef> {
32 let mut options = self.options.get(command).cloned().unwrap_or_default();
34
35 if let Some(common) = self.options.get("common") {
37 options.extend(common.clone());
38 }
39
40 options
41 }
42
43 fn validate(&self, command: &str, option: &str, value: Option<&str>) -> ValidationResult<()> {
44 let options = self.get_options(command);
45
46 let option_def = options
48 .iter()
49 .find(|def| def.name == option)
50 .ok_or_else(|| ValidationError::unknown_option(command, option, vec![]))?;
51
52 if let Some(deprecated_msg) = &option_def.deprecated {
54 return Err(ValidationError::deprecated(option, deprecated_msg));
55 }
56
57 match (&option_def.value_type, value) {
59 (OptionValueType::Flag, Some(val)) => Err(ValidationError::UnexpectedValue {
60 option: option.to_string(),
61 value: val.to_string(),
62 }),
63 (OptionValueType::Single(validator), Some(val)) => {
64 validator.validate(val).map_err(|mut e| {
65 if let ValidationError::InvalidValue {
66 option: ref mut opt,
67 ..
68 } = e
69 {
70 *opt = option.to_string();
71 }
72 e
73 })
74 }
75 (OptionValueType::Multiple(validator), Some(val)) => {
76 for v in val.split(',') {
78 validator.validate(v.trim()).map_err(|mut e| {
79 if let ValidationError::InvalidValue {
80 option: ref mut opt,
81 ..
82 } = e
83 {
84 *opt = option.to_string();
85 }
86 e
87 })?;
88 }
89 Ok(())
90 }
91 (OptionValueType::Single(_) | OptionValueType::Multiple(_), None) => {
92 Err(ValidationError::MissingValue {
93 option: option.to_string(),
94 })
95 }
96 (OptionValueType::Flag, None) => Ok(()),
97 }
98 }
99
100 fn get_commands(&self) -> Vec<String> {
101 vec![
102 "build".to_string(),
103 "test".to_string(),
104 "run".to_string(),
105 "check".to_string(),
106 "clippy".to_string(),
107 "doc".to_string(),
108 "clean".to_string(),
109 "bench".to_string(),
110 "install".to_string(),
111 "publish".to_string(),
112 ]
113 }
114}
115
116fn build_cargo_options() -> HashMap<String, Vec<OptionDef>> {
118 let mut options = HashMap::new();
119
120 options.insert(
122 "common".to_string(),
123 vec![
124 OptionDef::flag("--verbose", "Use verbose output (-v)"),
125 OptionDef::flag("-v", "Use verbose output"),
126 OptionDef::flag("--quiet", "Do not print cargo log messages"),
127 OptionDef::flag("-q", "Do not print cargo log messages"),
128 OptionDef::single(
129 "--color",
130 "Coloring",
131 ValueValidator::Enum(vec![
132 "auto".to_string(),
133 "always".to_string(),
134 "never".to_string(),
135 ]),
136 ),
137 OptionDef::flag("--frozen", "Require Cargo.lock and cache are up to date"),
138 OptionDef::flag("--locked", "Require Cargo.lock is up to date"),
139 OptionDef::flag("--offline", "Run without accessing the network"),
140 OptionDef::single(
141 "--config",
142 "Override a configuration value",
143 ValueValidator::Any,
144 ),
145 ],
146 );
147
148 options.insert(
150 "build".to_string(),
151 vec![
152 OptionDef::flag(
153 "--release",
154 "Build artifacts in release mode, with optimizations",
155 ),
156 OptionDef::flag("--dev", "Build artifacts in development mode"),
157 OptionDef::single(
158 "--target",
159 "Build for the target triple",
160 ValueValidator::Any,
161 ),
162 OptionDef::multiple(
163 "--features",
164 "Space or comma separated list of features to activate",
165 ValueValidator::Any,
166 ),
167 OptionDef::flag("--all-features", "Activate all available features"),
168 OptionDef::flag(
169 "--no-default-features",
170 "Do not activate the default feature",
171 ),
172 OptionDef::single(
173 "--bin",
174 "Build only the specified binary",
175 ValueValidator::Any,
176 ),
177 OptionDef::flag("--bins", "Build all binaries"),
178 OptionDef::single(
179 "--lib",
180 "Build only this package's library",
181 ValueValidator::Any,
182 ),
183 OptionDef::flag("--workspace", "Build all packages in the workspace"),
184 OptionDef::single("-j", "Number of parallel jobs", ValueValidator::Number)
185 .conflicts_with(vec!["--jobs".to_string()]),
186 OptionDef::single("--jobs", "Number of parallel jobs", ValueValidator::Number)
187 .conflicts_with(vec!["-j".to_string()]),
188 ],
189 );
190
191 options.insert(
193 "test".to_string(),
194 vec![
195 OptionDef::flag("--release", "Run tests in release mode"),
196 OptionDef::single(
197 "--target",
198 "Build for the target triple",
199 ValueValidator::Any,
200 ),
201 OptionDef::multiple(
202 "--features",
203 "Space or comma separated list of features to activate",
204 ValueValidator::Any,
205 ),
206 OptionDef::flag("--all-features", "Activate all available features"),
207 OptionDef::flag(
208 "--no-default-features",
209 "Do not activate the default feature",
210 ),
211 OptionDef::single(
212 "--test",
213 "Test only the specified test target",
214 ValueValidator::Any,
215 ),
216 OptionDef::flag("--tests", "Test all tests"),
217 OptionDef::single(
218 "--bin",
219 "Test only the specified binary",
220 ValueValidator::Any,
221 ),
222 OptionDef::flag("--bins", "Test all binaries"),
223 OptionDef::flag("--lib", "Test only this package's library"),
224 OptionDef::flag("--doc", "Test only this library's documentation"),
225 OptionDef::flag("--workspace", "Test all packages in the workspace"),
226 OptionDef::flag("--no-run", "Compile, but don't run tests"),
227 OptionDef::flag("--no-fail-fast", "Run all tests regardless of failure"),
228 ],
229 );
230
231 options.insert(
233 "run".to_string(),
234 vec![
235 OptionDef::flag("--release", "Run in release mode"),
236 OptionDef::single(
237 "--target",
238 "Build for the target triple",
239 ValueValidator::Any,
240 ),
241 OptionDef::multiple(
242 "--features",
243 "Space or comma separated list of features to activate",
244 ValueValidator::Any,
245 ),
246 OptionDef::flag("--all-features", "Activate all available features"),
247 OptionDef::flag(
248 "--no-default-features",
249 "Do not activate the default feature",
250 ),
251 OptionDef::single("--bin", "Run the specified binary", ValueValidator::Any),
252 ],
253 );
254
255 options.insert(
257 "check".to_string(),
258 vec![
259 OptionDef::flag("--release", "Check artifacts in release mode"),
260 OptionDef::single(
261 "--target",
262 "Check for the target triple",
263 ValueValidator::Any,
264 ),
265 OptionDef::multiple(
266 "--features",
267 "Space or comma separated list of features to activate",
268 ValueValidator::Any,
269 ),
270 OptionDef::flag("--all-features", "Activate all available features"),
271 OptionDef::flag(
272 "--no-default-features",
273 "Do not activate the default feature",
274 ),
275 OptionDef::single(
276 "--bin",
277 "Check only the specified binary",
278 ValueValidator::Any,
279 ),
280 OptionDef::flag("--bins", "Check all binaries"),
281 OptionDef::flag("--lib", "Check only this package's library"),
282 OptionDef::flag("--workspace", "Check all packages in the workspace"),
283 ],
284 );
285
286 options.insert(
288 "clippy".to_string(),
289 vec![
290 OptionDef::flag("--release", "Check artifacts in release mode"),
291 OptionDef::single(
292 "--target",
293 "Check for the target triple",
294 ValueValidator::Any,
295 ),
296 OptionDef::multiple(
297 "--features",
298 "Space or comma separated list of features to activate",
299 ValueValidator::Any,
300 ),
301 OptionDef::flag("--all-features", "Activate all available features"),
302 OptionDef::flag(
303 "--no-default-features",
304 "Do not activate the default feature",
305 ),
306 OptionDef::flag("--workspace", "Check all packages in the workspace"),
307 OptionDef::flag("--fix", "Automatically apply lint suggestions"),
308 ],
309 );
310
311 options
312}