1use std::{path::Path, process::Command};
2
3use cargo_metadata::{MetadataCommand, camino::Utf8PathBuf};
4
5use crate::cargo_style;
6
7#[derive(clap::Parser)]
8#[command(name = "cargo")]
9#[command(bin_name = "cargo")]
10#[command(styles = cargo_style::CLAP_STYLING)]
11pub enum Cli {
12 Flux {
14 #[command(flatten)]
15 check_opts: CheckOpts,
16
17 #[command(subcommand)]
18 command: Option<CargoFluxCommand>,
19
20 #[arg(short = 'V', long, action = clap::ArgAction::SetTrue)]
22 version: bool,
23
24 #[arg(short, long, action = clap::ArgAction::Count)]
26 verbose: u8,
27 },
28}
29
30#[derive(clap::Subcommand)]
31pub enum CargoFluxCommand {
32 Check(CheckOpts),
35 Clean(CleanOpts),
37}
38
39impl CargoFluxCommand {
40 pub fn forward_args(&self, cmd: &mut Command, config_file: &Path) {
41 match self {
42 CargoFluxCommand::Check(check_opts) => {
43 cmd.arg("check");
44 check_opts.forward_args(cmd);
45 }
46 CargoFluxCommand::Clean(clean_opts) => {
47 cmd.arg("clean");
48 clean_opts.forward_args(cmd);
49 }
50 }
51 cmd.args(["--profile", "flux"]);
52 cmd.args(["--config".as_ref(), config_file.as_os_str()]);
53 }
54
55 pub fn metadata(&self) -> MetadataCommand {
56 let mut meta = cargo_metadata::MetadataCommand::new();
57 match self {
58 CargoFluxCommand::Check(check_options) => {
59 check_options.forward_to_metadata(&mut meta);
60 }
61 CargoFluxCommand::Clean(clean_options) => {
62 clean_options.forward_to_metadata(&mut meta);
63 }
64 }
65 meta
66 }
67}
68
69#[derive(clap::Args)]
70pub struct CheckOpts {
71 #[arg(long, value_name = "FMT")]
73 message_format: Option<String>,
74
75 #[command(flatten)]
76 workspace: Workspace,
77 #[command(flatten)]
78 features: Features,
79 #[command(flatten)]
80 compilation: CompilationOptions,
81 #[command(flatten)]
82 manifest: ManifestOptions,
83}
84
85impl CheckOpts {
86 fn forward_args(&self, cmd: &mut Command) {
87 let CheckOpts { message_format, workspace, features, compilation, manifest } = self;
88 if let Some(message_format) = &message_format {
89 cmd.args(["--message-format", message_format]);
90 }
91 workspace.forward_args(cmd);
92 features.forward_args(cmd);
93 compilation.forward_args(cmd);
94 manifest.forward_args(cmd);
95 }
96
97 fn forward_to_metadata(&self, meta: &mut MetadataCommand) {
98 let CheckOpts { features, manifest, .. } = self;
99 features.forward_to_metadata(meta);
100 manifest.forward_to_metadata(meta);
101 }
102}
103
104#[derive(clap::Args)]
105pub struct CleanOpts {
106 #[command(flatten, next_help_heading = "Package Selection")]
107 package: Package,
108 #[command(flatten)]
109 features: Features,
110 #[command(flatten)]
111 manifest: ManifestOptions,
112}
113
114impl CleanOpts {
115 fn forward_args(&self, cmd: &mut Command) {
116 let CleanOpts { package, features, manifest } = self;
117 package.forward_args(cmd);
118 features.forward_args(cmd);
119 manifest.forward_args(cmd);
120 }
121
122 fn forward_to_metadata(&self, meta: &mut MetadataCommand) {
123 let CleanOpts { package: _, features, manifest } = self;
124 features.forward_to_metadata(meta);
125 manifest.forward_to_metadata(meta);
126 }
127}
128
129#[derive(Debug, clap::Args)]
130#[command(about = None, long_about = None, next_help_heading = "Package Selection")]
131pub struct Workspace {
132 #[command(flatten)]
133 pub package: Package,
134
135 #[arg(long)]
136 pub workspace: bool,
138
139 #[arg(long, value_name = "SPEC")]
140 pub exclude: Vec<String>,
142}
143
144impl Workspace {
145 fn forward_args(&self, cmd: &mut Command) {
146 let Workspace { package, workspace, exclude } = self;
147 package.forward_args(cmd);
148 if *workspace {
149 cmd.arg("--workspace");
150 }
151 if !exclude.is_empty() {
152 cmd.args(exclude.iter().flat_map(|package| ["--exclude", package]));
153 }
154 }
155}
156
157#[derive(Debug, clap::Args)]
158#[command(about = None, long_about = None)]
159pub struct Package {
160 #[arg(short, long, value_name = "SPEC")]
161 pub package: Vec<String>,
163}
164
165impl Package {
166 fn forward_args(&self, cmd: &mut Command) {
167 let Package { package } = self;
168 if !package.is_empty() {
169 cmd.args(package.iter().flat_map(|package| ["--package", package]));
170 }
171 }
172}
173
174#[derive(Default, Clone, Debug, PartialEq, Eq, clap::Args)]
175#[command(about = None, long_about = None, next_help_heading = "Feature Selection")]
176pub struct Features {
177 #[arg(short = 'F', long, value_delimiter = ' ')]
178 pub features: Vec<String>,
180 #[arg(long)]
181 pub all_features: bool,
183 #[arg(long)]
184 pub no_default_features: bool,
186}
187
188impl Features {
189 fn forward_args(&self, cmd: &mut Command) {
190 let Features { features, all_features, no_default_features } = self;
191 if !features.is_empty() {
192 cmd.args(features.iter().flat_map(|feature| ["--features", feature]));
193 }
194 if *all_features {
195 cmd.arg("--all-features");
196 }
197 if *no_default_features {
198 cmd.arg("--no-default-features");
199 }
200 }
201
202 fn forward_to_metadata(&self, meta: &mut MetadataCommand) {
203 let Features { features, all_features, no_default_features } = self;
204 if *all_features {
205 meta.features(cargo_metadata::CargoOpt::AllFeatures);
206 }
207 if *no_default_features {
208 meta.features(cargo_metadata::CargoOpt::NoDefaultFeatures);
209 }
210 if !features.is_empty() {
211 meta.features(cargo_metadata::CargoOpt::SomeFeatures(features.clone()));
212 }
213 }
214}
215
216#[derive(Debug, clap::Args)]
217#[command(next_help_heading = "Compilation Options")]
218pub struct CompilationOptions {
219 #[arg(short = 'j', long, value_name = "N")]
220 pub jobs: Option<u32>,
222 #[arg(long)]
223 pub keep_going: bool,
225 #[arg(long, value_name = "TRIPLE")]
226 pub target: Vec<String>,
228}
229
230impl CompilationOptions {
231 fn forward_args(&self, cmd: &mut Command) {
232 let CompilationOptions { jobs, keep_going, target } = self;
233 if let Some(jobs) = jobs {
234 cmd.args(["--jobs", &format!("{jobs}")]);
235 }
236 if *keep_going {
237 cmd.arg("--keep-going");
238 }
239 for t in target {
240 cmd.args(["--target", t]);
241 }
242 }
243}
244
245#[derive(Debug, clap::Args)]
246#[command(next_help_heading = "Manifest Options")]
247pub struct ManifestOptions {
248 #[arg(long, name = "PATH")]
249 manifest_path: Option<Utf8PathBuf>,
251 #[arg(long)]
253 offline: bool,
254}
255
256impl ManifestOptions {
257 fn forward_args(&self, cmd: &mut Command) {
258 let ManifestOptions { manifest_path, offline } = self;
259 if let Some(manifest_path) = &manifest_path {
260 cmd.args(["--manifest-path", manifest_path.as_str()]);
261 }
262 if *offline {
263 cmd.arg("--offline");
264 }
265 }
266
267 fn forward_to_metadata(&self, meta: &mut MetadataCommand) {
268 let ManifestOptions { manifest_path, offline: _ } = self;
270 if let Some(manifest_path) = &manifest_path {
271 meta.manifest_path(manifest_path);
272 }
273 }
274}