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