raz_core/
cargo_options_catalog.rs

1//! Complete catalog of all cargo subcommand options
2//!
3//! This module contains a comprehensive list of all cargo options
4//! extracted from cargo help commands for intelligent parsing.
5
6use once_cell::sync::Lazy;
7use std::collections::HashMap;
8
9/// Type of option value
10#[derive(Debug, Clone, PartialEq)]
11pub enum OptionValueType {
12    /// No value (boolean flag)
13    Flag,
14    /// Single value required
15    Required(String), // description of expected value
16    /// Optional value
17    Optional(String), // description of expected value
18    /// Multiple values allowed
19    Multiple(String), // description of expected value
20}
21
22/// Option metadata
23#[derive(Debug, Clone)]
24pub struct OptionInfo {
25    /// Long form (e.g., "--release")
26    pub long: &'static str,
27    /// Short form if available (e.g., "-r")
28    pub short: Option<&'static str>,
29    /// Value type
30    pub value_type: OptionValueType,
31    /// Description
32    pub description: &'static str,
33    /// Category (Package Selection, Target Selection, etc.)
34    pub category: &'static str,
35}
36
37/// Complete catalog of cargo options by subcommand
38pub static CARGO_OPTIONS_CATALOG: Lazy<HashMap<&'static str, Vec<OptionInfo>>> = Lazy::new(|| {
39    let mut catalog = HashMap::new();
40
41    // Common options shared by all subcommands
42    let common_options = vec![
43        OptionInfo {
44            long: "--message-format",
45            short: None,
46            value_type: OptionValueType::Required("FMT".to_string()),
47            description: "Error format",
48            category: "Options",
49        },
50        OptionInfo {
51            long: "--verbose",
52            short: Some("-v"),
53            value_type: OptionValueType::Flag,
54            description: "Use verbose output (-vv very verbose/build.rs output)",
55            category: "Options",
56        },
57        OptionInfo {
58            long: "--quiet",
59            short: Some("-q"),
60            value_type: OptionValueType::Flag,
61            description: "Do not print cargo log messages",
62            category: "Options",
63        },
64        OptionInfo {
65            long: "--color",
66            short: None,
67            value_type: OptionValueType::Required("WHEN".to_string()),
68            description: "Coloring: auto, always, never",
69            category: "Options",
70        },
71        OptionInfo {
72            long: "--config",
73            short: None,
74            value_type: OptionValueType::Required("KEY=VALUE|PATH".to_string()),
75            description: "Override a configuration value",
76            category: "Options",
77        },
78        OptionInfo {
79            long: "-Z",
80            short: None,
81            value_type: OptionValueType::Required("FLAG".to_string()),
82            description: "Unstable (nightly-only) flags",
83            category: "Options",
84        },
85    ];
86
87    // Package selection options
88    let package_options = vec![
89        OptionInfo {
90            long: "--package",
91            short: Some("-p"),
92            value_type: OptionValueType::Optional("SPEC".to_string()),
93            description: "Package to operate on",
94            category: "Package Selection",
95        },
96        OptionInfo {
97            long: "--workspace",
98            short: None,
99            value_type: OptionValueType::Flag,
100            description: "Operate on all packages in the workspace",
101            category: "Package Selection",
102        },
103        OptionInfo {
104            long: "--exclude",
105            short: None,
106            value_type: OptionValueType::Required("SPEC".to_string()),
107            description: "Exclude packages from the operation",
108            category: "Package Selection",
109        },
110        OptionInfo {
111            long: "--all",
112            short: None,
113            value_type: OptionValueType::Flag,
114            description: "Alias for --workspace (deprecated)",
115            category: "Package Selection",
116        },
117    ];
118
119    // Feature selection options
120    let feature_options = vec![
121        OptionInfo {
122            long: "--features",
123            short: Some("-F"),
124            value_type: OptionValueType::Required("FEATURES".to_string()),
125            description: "Space or comma separated list of features to activate",
126            category: "Feature Selection",
127        },
128        OptionInfo {
129            long: "--all-features",
130            short: None,
131            value_type: OptionValueType::Flag,
132            description: "Activate all available features",
133            category: "Feature Selection",
134        },
135        OptionInfo {
136            long: "--no-default-features",
137            short: None,
138            value_type: OptionValueType::Flag,
139            description: "Do not activate the default feature",
140            category: "Feature Selection",
141        },
142    ];
143
144    // Compilation options
145    let compilation_options = vec![
146        OptionInfo {
147            long: "--jobs",
148            short: Some("-j"),
149            value_type: OptionValueType::Required("N".to_string()),
150            description: "Number of parallel jobs",
151            category: "Compilation Options",
152        },
153        OptionInfo {
154            long: "--release",
155            short: Some("-r"),
156            value_type: OptionValueType::Flag,
157            description: "Build artifacts in release mode",
158            category: "Compilation Options",
159        },
160        OptionInfo {
161            long: "--profile",
162            short: None,
163            value_type: OptionValueType::Required("PROFILE-NAME".to_string()),
164            description: "Build artifacts with the specified profile",
165            category: "Compilation Options",
166        },
167        OptionInfo {
168            long: "--target",
169            short: None,
170            value_type: OptionValueType::Optional("TRIPLE".to_string()),
171            description: "Build for the target triple",
172            category: "Compilation Options",
173        },
174        OptionInfo {
175            long: "--target-dir",
176            short: None,
177            value_type: OptionValueType::Required("DIRECTORY".to_string()),
178            description: "Directory for all generated artifacts",
179            category: "Compilation Options",
180        },
181    ];
182
183    // Manifest options
184    let manifest_options = vec![
185        OptionInfo {
186            long: "--manifest-path",
187            short: None,
188            value_type: OptionValueType::Required("PATH".to_string()),
189            description: "Path to Cargo.toml",
190            category: "Manifest Options",
191        },
192        OptionInfo {
193            long: "--locked",
194            short: None,
195            value_type: OptionValueType::Flag,
196            description: "Assert that Cargo.lock will remain unchanged",
197            category: "Manifest Options",
198        },
199        OptionInfo {
200            long: "--offline",
201            short: None,
202            value_type: OptionValueType::Flag,
203            description: "Run without accessing the network",
204            category: "Manifest Options",
205        },
206        OptionInfo {
207            long: "--frozen",
208            short: None,
209            value_type: OptionValueType::Flag,
210            description: "Equivalent to specifying both --locked and --offline",
211            category: "Manifest Options",
212        },
213    ];
214
215    // cargo run specific options
216    let mut run_options = vec![];
217    run_options.extend(common_options.clone());
218    run_options.extend(package_options.clone());
219    run_options.extend(feature_options.clone());
220    run_options.extend(compilation_options.clone());
221    run_options.extend(manifest_options.clone());
222    run_options.extend(vec![
223        OptionInfo {
224            long: "--bin",
225            short: None,
226            value_type: OptionValueType::Optional("NAME".to_string()),
227            description: "Name of the bin target to run",
228            category: "Target Selection",
229        },
230        OptionInfo {
231            long: "--example",
232            short: None,
233            value_type: OptionValueType::Optional("NAME".to_string()),
234            description: "Name of the example target to run",
235            category: "Target Selection",
236        },
237    ]);
238    catalog.insert("run", run_options);
239
240    // cargo test specific options
241    let mut test_options = vec![];
242    test_options.extend(common_options.clone());
243    test_options.extend(package_options.clone());
244    test_options.extend(feature_options.clone());
245    test_options.extend(compilation_options.clone());
246    test_options.extend(manifest_options.clone());
247    test_options.extend(vec![
248        OptionInfo {
249            long: "--no-run",
250            short: None,
251            value_type: OptionValueType::Flag,
252            description: "Compile, but don't run tests",
253            category: "Options",
254        },
255        OptionInfo {
256            long: "--no-fail-fast",
257            short: None,
258            value_type: OptionValueType::Flag,
259            description: "Run all tests regardless of failure",
260            category: "Options",
261        },
262        OptionInfo {
263            long: "--lib",
264            short: None,
265            value_type: OptionValueType::Flag,
266            description: "Test only this package's library",
267            category: "Target Selection",
268        },
269        OptionInfo {
270            long: "--bins",
271            short: None,
272            value_type: OptionValueType::Flag,
273            description: "Test all binaries",
274            category: "Target Selection",
275        },
276        OptionInfo {
277            long: "--bin",
278            short: None,
279            value_type: OptionValueType::Optional("NAME".to_string()),
280            description: "Test only the specified binary",
281            category: "Target Selection",
282        },
283        OptionInfo {
284            long: "--examples",
285            short: None,
286            value_type: OptionValueType::Flag,
287            description: "Test all examples",
288            category: "Target Selection",
289        },
290        OptionInfo {
291            long: "--example",
292            short: None,
293            value_type: OptionValueType::Optional("NAME".to_string()),
294            description: "Test only the specified example",
295            category: "Target Selection",
296        },
297        OptionInfo {
298            long: "--tests",
299            short: None,
300            value_type: OptionValueType::Flag,
301            description: "Test all test targets",
302            category: "Target Selection",
303        },
304        OptionInfo {
305            long: "--test",
306            short: None,
307            value_type: OptionValueType::Optional("NAME".to_string()),
308            description: "Test only the specified test target",
309            category: "Target Selection",
310        },
311        OptionInfo {
312            long: "--benches",
313            short: None,
314            value_type: OptionValueType::Flag,
315            description: "Test all bench targets",
316            category: "Target Selection",
317        },
318        OptionInfo {
319            long: "--bench",
320            short: None,
321            value_type: OptionValueType::Optional("NAME".to_string()),
322            description: "Test only the specified bench target",
323            category: "Target Selection",
324        },
325        OptionInfo {
326            long: "--all-targets",
327            short: None,
328            value_type: OptionValueType::Flag,
329            description: "Test all targets (does not include doctests)",
330            category: "Target Selection",
331        },
332        OptionInfo {
333            long: "--doc",
334            short: None,
335            value_type: OptionValueType::Flag,
336            description: "Test only this library's documentation",
337            category: "Target Selection",
338        },
339    ]);
340    catalog.insert("test", test_options);
341
342    // cargo build specific options
343    let mut build_options = vec![];
344    build_options.extend(common_options.clone());
345    build_options.extend(package_options.clone());
346    build_options.extend(feature_options.clone());
347    build_options.extend(compilation_options.clone());
348    build_options.extend(manifest_options.clone());
349    build_options.extend(vec![
350        OptionInfo {
351            long: "--lib",
352            short: None,
353            value_type: OptionValueType::Flag,
354            description: "Build only this package's library",
355            category: "Target Selection",
356        },
357        OptionInfo {
358            long: "--bins",
359            short: None,
360            value_type: OptionValueType::Flag,
361            description: "Build all binaries",
362            category: "Target Selection",
363        },
364        OptionInfo {
365            long: "--bin",
366            short: None,
367            value_type: OptionValueType::Optional("NAME".to_string()),
368            description: "Build only the specified binary",
369            category: "Target Selection",
370        },
371        OptionInfo {
372            long: "--examples",
373            short: None,
374            value_type: OptionValueType::Flag,
375            description: "Build all examples",
376            category: "Target Selection",
377        },
378        OptionInfo {
379            long: "--example",
380            short: None,
381            value_type: OptionValueType::Optional("NAME".to_string()),
382            description: "Build only the specified example",
383            category: "Target Selection",
384        },
385        OptionInfo {
386            long: "--tests",
387            short: None,
388            value_type: OptionValueType::Flag,
389            description: "Build all test targets",
390            category: "Target Selection",
391        },
392        OptionInfo {
393            long: "--test",
394            short: None,
395            value_type: OptionValueType::Optional("NAME".to_string()),
396            description: "Build only the specified test target",
397            category: "Target Selection",
398        },
399        OptionInfo {
400            long: "--benches",
401            short: None,
402            value_type: OptionValueType::Flag,
403            description: "Build all bench targets",
404            category: "Target Selection",
405        },
406        OptionInfo {
407            long: "--bench",
408            short: None,
409            value_type: OptionValueType::Optional("NAME".to_string()),
410            description: "Build only the specified bench target",
411            category: "Target Selection",
412        },
413        OptionInfo {
414            long: "--all-targets",
415            short: None,
416            value_type: OptionValueType::Flag,
417            description: "Build all targets",
418            category: "Target Selection",
419        },
420        OptionInfo {
421            long: "--keep-going",
422            short: None,
423            value_type: OptionValueType::Flag,
424            description: "Do not abort the build as soon as there is an error",
425            category: "Compilation Options",
426        },
427    ]);
428    catalog.insert("build", build_options);
429
430    // cargo bench specific options (similar to test)
431    let mut bench_options = vec![];
432    bench_options.extend(common_options.clone());
433    bench_options.extend(package_options.clone());
434    bench_options.extend(feature_options.clone());
435    bench_options.extend(compilation_options.clone());
436    bench_options.extend(manifest_options.clone());
437    bench_options.extend(vec![
438        OptionInfo {
439            long: "--no-run",
440            short: None,
441            value_type: OptionValueType::Flag,
442            description: "Compile, but don't run benchmarks",
443            category: "Options",
444        },
445        OptionInfo {
446            long: "--no-fail-fast",
447            short: None,
448            value_type: OptionValueType::Flag,
449            description: "Run all benchmarks regardless of failure",
450            category: "Options",
451        },
452        // Target selection options same as test
453        OptionInfo {
454            long: "--lib",
455            short: None,
456            value_type: OptionValueType::Flag,
457            description: "Benchmark only this package's library",
458            category: "Target Selection",
459        },
460        OptionInfo {
461            long: "--bins",
462            short: None,
463            value_type: OptionValueType::Flag,
464            description: "Benchmark all binaries",
465            category: "Target Selection",
466        },
467        OptionInfo {
468            long: "--bin",
469            short: None,
470            value_type: OptionValueType::Optional("NAME".to_string()),
471            description: "Benchmark only the specified binary",
472            category: "Target Selection",
473        },
474        OptionInfo {
475            long: "--examples",
476            short: None,
477            value_type: OptionValueType::Flag,
478            description: "Benchmark all examples",
479            category: "Target Selection",
480        },
481        OptionInfo {
482            long: "--example",
483            short: None,
484            value_type: OptionValueType::Optional("NAME".to_string()),
485            description: "Benchmark only the specified example",
486            category: "Target Selection",
487        },
488        OptionInfo {
489            long: "--tests",
490            short: None,
491            value_type: OptionValueType::Flag,
492            description: "Benchmark all test targets",
493            category: "Target Selection",
494        },
495        OptionInfo {
496            long: "--test",
497            short: None,
498            value_type: OptionValueType::Optional("NAME".to_string()),
499            description: "Benchmark only the specified test target",
500            category: "Target Selection",
501        },
502        OptionInfo {
503            long: "--benches",
504            short: None,
505            value_type: OptionValueType::Flag,
506            description: "Benchmark all bench targets",
507            category: "Target Selection",
508        },
509        OptionInfo {
510            long: "--bench",
511            short: None,
512            value_type: OptionValueType::Optional("NAME".to_string()),
513            description: "Benchmark only the specified bench target",
514            category: "Target Selection",
515        },
516        OptionInfo {
517            long: "--all-targets",
518            short: None,
519            value_type: OptionValueType::Flag,
520            description: "Benchmark all targets",
521            category: "Target Selection",
522        },
523    ]);
524    catalog.insert("bench", bench_options);
525
526    // cargo check specific options
527    let mut check_options = vec![];
528    check_options.extend(common_options.clone());
529    check_options.extend(package_options.clone());
530    check_options.extend(feature_options.clone());
531    check_options.extend(compilation_options.clone());
532    check_options.extend(manifest_options);
533    check_options.extend(vec![
534        OptionInfo {
535            long: "--lib",
536            short: None,
537            value_type: OptionValueType::Flag,
538            description: "Check only this package's library",
539            category: "Target Selection",
540        },
541        OptionInfo {
542            long: "--bins",
543            short: None,
544            value_type: OptionValueType::Flag,
545            description: "Check all binaries",
546            category: "Target Selection",
547        },
548        OptionInfo {
549            long: "--bin",
550            short: None,
551            value_type: OptionValueType::Optional("NAME".to_string()),
552            description: "Check only the specified binary",
553            category: "Target Selection",
554        },
555        OptionInfo {
556            long: "--examples",
557            short: None,
558            value_type: OptionValueType::Flag,
559            description: "Check all examples",
560            category: "Target Selection",
561        },
562        OptionInfo {
563            long: "--example",
564            short: None,
565            value_type: OptionValueType::Optional("NAME".to_string()),
566            description: "Check only the specified example",
567            category: "Target Selection",
568        },
569        OptionInfo {
570            long: "--tests",
571            short: None,
572            value_type: OptionValueType::Flag,
573            description: "Check all test targets",
574            category: "Target Selection",
575        },
576        OptionInfo {
577            long: "--test",
578            short: None,
579            value_type: OptionValueType::Optional("NAME".to_string()),
580            description: "Check only the specified test target",
581            category: "Target Selection",
582        },
583        OptionInfo {
584            long: "--benches",
585            short: None,
586            value_type: OptionValueType::Flag,
587            description: "Check all bench targets",
588            category: "Target Selection",
589        },
590        OptionInfo {
591            long: "--bench",
592            short: None,
593            value_type: OptionValueType::Optional("NAME".to_string()),
594            description: "Check only the specified bench target",
595            category: "Target Selection",
596        },
597        OptionInfo {
598            long: "--all-targets",
599            short: None,
600            value_type: OptionValueType::Flag,
601            description: "Check all targets",
602            category: "Target Selection",
603        },
604        OptionInfo {
605            long: "--keep-going",
606            short: None,
607            value_type: OptionValueType::Flag,
608            description: "Do not abort the build as soon as there is an error",
609            category: "Compilation Options",
610        },
611    ]);
612    catalog.insert("check", check_options);
613
614    catalog
615});
616
617/// Check if a string is a valid cargo option for the given subcommand
618pub fn is_cargo_option(subcommand: &str, option: &str) -> bool {
619    if let Some(options) = CARGO_OPTIONS_CATALOG.get(subcommand) {
620        options
621            .iter()
622            .any(|opt| opt.long == option || (opt.short == Some(option)))
623    } else {
624        false
625    }
626}
627
628/// Get option info for a given subcommand and option
629pub fn get_option_info(subcommand: &str, option: &str) -> Option<&'static OptionInfo> {
630    CARGO_OPTIONS_CATALOG
631        .get(subcommand)?
632        .iter()
633        .find(|opt| opt.long == option || (opt.short == Some(option)))
634}
635
636/// Parse an option and its value from tokens
637pub fn parse_option_value(
638    option: &str,
639    tokens: &[String],
640    index: usize,
641) -> (Option<String>, usize) {
642    if let Some(info) = CARGO_OPTIONS_CATALOG
643        .values()
644        .flatten()
645        .find(|opt| opt.long == option || (opt.short == Some(option)))
646    {
647        match &info.value_type {
648            OptionValueType::Flag => (None, 0),
649            OptionValueType::Required(_) => {
650                if index + 1 < tokens.len() && !tokens[index + 1].starts_with('-') {
651                    (Some(tokens[index + 1].clone()), 1)
652                } else {
653                    // Error: required value missing
654                    (None, 0)
655                }
656            }
657            OptionValueType::Optional(_) => {
658                if index + 1 < tokens.len() && !tokens[index + 1].starts_with('-') {
659                    (Some(tokens[index + 1].clone()), 1)
660                } else {
661                    (None, 0)
662                }
663            }
664            OptionValueType::Multiple(_) => {
665                // For multiple values, take all non-option tokens
666                let mut values = vec![];
667                let mut consumed = 0;
668                for token in tokens.iter().skip(index + 1) {
669                    if token.starts_with('-') {
670                        break;
671                    }
672                    values.push(token.clone());
673                    consumed += 1;
674                }
675                if values.is_empty() {
676                    (None, 0)
677                } else {
678                    (Some(values.join(" ")), consumed)
679                }
680            }
681        }
682    } else {
683        (None, 0)
684    }
685}