raz_validation/provider/
cargo.rs

1//! Cargo options provider
2
3use crate::error::{ValidationError, ValidationResult};
4use crate::provider::{OptionDef, OptionProvider, OptionValueType, ValueValidator};
5use std::collections::HashMap;
6
7/// Provider for cargo command options
8pub 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        // Get command-specific options
33        let mut options = self.options.get(command).cloned().unwrap_or_default();
34
35        // Add common cargo options that work with most commands
36        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        // Find the option definition
47        let option_def = options
48            .iter()
49            .find(|def| def.name == option)
50            .ok_or_else(|| ValidationError::unknown_option(command, option, vec![]))?;
51
52        // Check if deprecated
53        if let Some(deprecated_msg) = &option_def.deprecated {
54            return Err(ValidationError::deprecated(option, deprecated_msg));
55        }
56
57        // Validate value based on option type
58        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 multiple values, validate each comma-separated value
77                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
116/// Build the cargo options catalog
117fn build_cargo_options() -> HashMap<String, Vec<OptionDef>> {
118    let mut options = HashMap::new();
119
120    // Common options that work with most cargo commands
121    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    // Build command options
149    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    // Test command options
192    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    // Run command options
232    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    // Check command options (similar to build)
256    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    // Clippy command options
287    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}