Skip to main content

cargo_fixit/util/
cli.rs

1use clap::Parser;
2
3#[derive(Debug, Parser)]
4pub struct CheckFlags {
5    /// Package(s) to fix
6    #[arg(short, long, value_name = "SPEC", help_heading = "Package Selection")]
7    package: Vec<String>,
8
9    /// Fix all packages in the workspace
10    #[arg(long, help_heading = "Package Selection")]
11    workspace: bool,
12
13    /// Exclude packages from the fixes
14    #[arg(long, value_name = "SPEC", help_heading = "Package Selection")]
15    exclude: Vec<String>,
16
17    /// Alias for --workspace (deprecated)
18    #[arg(long, help_heading = "Package Selection")]
19    all: bool,
20
21    /// Fix only this package's library
22    #[arg(long, help_heading = "Target Selection")]
23    lib: bool,
24
25    /// Fix all binaries
26    #[arg(long, help_heading = "Target Selection")]
27    bins: bool,
28
29    /// Fix only the specified binary
30    #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
31    bin: Option<String>,
32
33    /// Fix all examples
34    #[arg(long, help_heading = "Target Selection")]
35    examples: bool,
36
37    /// Fix only the specified binary
38    #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
39    example: Option<String>,
40
41    /// Fix all tests
42    #[arg(long, help_heading = "Target Selection")]
43    tests: bool,
44
45    /// Fix only the specified test
46    #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
47    test: Option<String>,
48
49    /// Fix all benches
50    #[arg(long, help_heading = "Target Selection")]
51    benches: bool,
52
53    /// Fix only the specified bench
54    #[arg(long, value_name = "NAME", help_heading = "Target Selection")]
55    bench: Option<String>,
56
57    /// Fix all targets
58    #[arg(long, help_heading = "Target Selection")]
59    all_targets: bool,
60
61    /// Space or comma separated list of features to activate
62    #[arg(
63        short = 'F',
64        long,
65        value_name = "FEATURES",
66        help_heading = "Feature Selection"
67    )]
68    features: Vec<String>,
69
70    /// Activate all available features
71    #[arg(long, help_heading = "Feature Selection")]
72    all_features: bool,
73
74    /// Do not activate the `default` feature
75    #[arg(long, help_heading = "Feature Selection")]
76    no_default_features: bool,
77
78    /// Unstable (nightly-only) flags
79    #[arg(short = 'Z', value_name = "FLAG")]
80    unstable_flags: Vec<String>,
81
82    /// Number of parallel jobs, defaults to # of CPUs.
83    #[arg(long, value_name = "N", help_heading = "Compilation Options")]
84    jobs: Option<usize>,
85
86    /// Fix artifacts in release mode, with optimizations
87    #[arg(long, help_heading = "Compilation Options")]
88    release: bool,
89
90    /// Build artifacts with the specified profile
91    #[arg(
92        long,
93        value_name = "PROFILE-NAME",
94        help_heading = "Compilation Options"
95    )]
96    profile: Option<String>,
97
98    /// Fix for the target triple
99    #[arg(long, value_name = "TRIPLE", help_heading = "Compilation Options")]
100    target: Vec<String>,
101
102    /// Directory for all generated artifacts
103    #[arg(long, value_name = "DIRECTORY", help_heading = "Compilation Options")]
104    target_dir: Option<String>,
105
106    /// Path to Cargo.toml
107    #[arg(long, value_name = "PATH", help_heading = "Manifest Options")]
108    manifest_path: Option<String>,
109
110    /// Path to Cargo.lock (unstable)
111    #[arg(long, value_name = "PATH", help_heading = "Manifest Options")]
112    lockfile_path: Option<String>,
113
114    /// Ignore `rust-version` specification in packages
115    #[arg(long, help_heading = "Manifest Options")]
116    ignore_rust_version: bool,
117
118    /// Assert that `Cargo.lock` will remain unchanged
119    #[arg(long, help_heading = "Manifest Options")]
120    locked: bool,
121
122    /// Run without accessing the network
123    #[arg(long, help_heading = "Manifest Options")]
124    offline: bool,
125
126    /// Equivalent to specifying both --locked and --offline
127    #[arg(long, help_heading = "Manifest Options")]
128    frozen: bool,
129}
130
131/// Package selectors that determine which workspace packages Cargo treats as primary.
132#[derive(Debug)]
133pub(crate) enum PackageSelection<'a> {
134    Default,
135    Workspace { exclude: &'a [String] },
136    Packages(&'a [String]),
137}
138
139impl CheckFlags {
140    pub(crate) fn package_selection(&self) -> PackageSelection<'_> {
141        if self.workspace || self.all {
142            PackageSelection::Workspace {
143                exclude: &self.exclude,
144            }
145        } else if self.package.is_empty() {
146            debug_assert!(self.exclude.is_empty());
147            PackageSelection::Default
148        } else {
149            debug_assert!(self.exclude.is_empty());
150            PackageSelection::Packages(&self.package)
151        }
152    }
153
154    pub fn to_flags(&self) -> Vec<String> {
155        let mut out = Vec::new();
156
157        for spec in self.package.clone() {
158            out.push("--package".to_owned());
159            out.push(spec);
160        }
161        if self.workspace {
162            out.push("--workspace".to_owned());
163        }
164        for spec in self.exclude.clone() {
165            out.push("--exclude".to_owned());
166            out.push(spec);
167        }
168        if self.all {
169            out.push("--all".to_owned());
170        }
171
172        if self.lib {
173            out.push("--lib".to_owned());
174        }
175
176        if self.bins {
177            out.push("--bins".to_owned());
178        }
179        if let Some(b) = self.bin.clone() {
180            out.push("--bin".to_owned());
181            out.push(b);
182        }
183
184        if self.examples {
185            out.push("--examples".to_owned());
186        }
187        if let Some(b) = self.example.clone() {
188            out.push("--example".to_owned());
189            out.push(b);
190        }
191
192        if self.tests {
193            out.push("--tests".to_owned());
194        }
195        if let Some(b) = self.test.clone() {
196            out.push("--test".to_owned());
197            out.push(b);
198        }
199
200        if self.benches {
201            out.push("--benches".to_owned());
202        }
203        if let Some(b) = self.bench.clone() {
204            out.push("--bench".to_owned());
205            out.push(b);
206        }
207
208        if self.all_targets {
209            out.push("--all-targets".to_owned());
210        }
211
212        for i in self.features.clone() {
213            out.push("--features".to_owned());
214            out.push(i);
215        }
216        if self.all_features {
217            out.push("--all-features".to_owned());
218        }
219        if self.no_default_features {
220            out.push("--no-default-features".to_owned());
221        }
222
223        for i in self.unstable_flags.clone() {
224            out.push("-Z".to_owned());
225            out.push(i);
226        }
227
228        if let Some(b) = self.jobs {
229            out.push("--jobs".to_owned());
230            out.push(b.to_string());
231        }
232        if self.release {
233            out.push("--release".to_owned());
234        }
235        if let Some(b) = self.profile.clone() {
236            out.push("--profile".to_owned());
237            out.push(b);
238        }
239
240        for spec in self.target.clone() {
241            out.push("--target".to_owned());
242            out.push(spec);
243        }
244        if let Some(b) = self.target_dir.clone() {
245            out.push("--target-dir".to_owned());
246            out.push(b);
247        }
248
249        if let Some(b) = self.manifest_path.clone() {
250            out.push("--manifest-path".to_owned());
251            out.push(b);
252        }
253        if let Some(b) = self.lockfile_path.clone() {
254            out.push("--lockfile-path".to_owned());
255            out.push(b);
256        }
257        if self.ignore_rust_version {
258            out.push("--ignore-rust-version".to_owned());
259        }
260        if self.locked {
261            out.push("--locked".to_owned());
262        }
263        if self.offline {
264            out.push("--offline".to_owned());
265        }
266        if self.frozen {
267            out.push("--frozen".to_owned());
268        }
269        out
270    }
271
272    /// Returns flags that can affect dependency resolution.
273    ///
274    /// Package and target filters are omitted so the resulting graph stays conservative.
275    pub(crate) fn to_metadata_flags(&self) -> Vec<String> {
276        let mut out = Vec::new();
277
278        for feature in &self.features {
279            out.push("--features".to_owned());
280            out.push(feature.clone());
281        }
282        if self.all_features {
283            out.push("--all-features".to_owned());
284        }
285        if self.no_default_features {
286            out.push("--no-default-features".to_owned());
287        }
288
289        for flag in &self.unstable_flags {
290            out.push("-Z".to_owned());
291            out.push(flag.clone());
292        }
293
294        if let Some(path) = &self.manifest_path {
295            out.push("--manifest-path".to_owned());
296            out.push(path.clone());
297        }
298        if let Some(path) = &self.lockfile_path {
299            out.push("--lockfile-path".to_owned());
300            out.push(path.clone());
301        }
302        if self.locked {
303            out.push("--locked".to_owned());
304        }
305        if self.offline {
306            out.push("--offline".to_owned());
307        }
308        if self.frozen {
309            out.push("--frozen".to_owned());
310        }
311
312        out
313    }
314}