1use std::path::PathBuf;
2use std::process::Command;
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use crate::heading;
8use clap::{ArgAction, Parser};
9
10#[derive(Clone, Debug, Default, Parser)]
12#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
13pub struct CommonOptions {
14 #[arg(short = 'q', long)]
16 #[cfg_attr(feature = "serde", serde(default))]
17 pub quiet: bool,
18
19 #[arg(
21 short = 'j',
22 long,
23 value_name = "N",
24 allow_hyphen_values = true,
25 help_heading = heading::COMPILATION_OPTIONS,
26 )]
27 #[cfg_attr(feature = "serde", serde(default))]
28 pub jobs: Option<usize>,
29
30 #[arg(long, help_heading = heading::COMPILATION_OPTIONS)]
32 #[cfg_attr(feature = "serde", serde(default))]
33 pub keep_going: bool,
34
35 #[arg(
37 long,
38 value_name = "PROFILE-NAME",
39 help_heading = heading::COMPILATION_OPTIONS,
40 )]
41 #[cfg_attr(feature = "serde", serde(default))]
42 pub profile: Option<String>,
43
44 #[arg(
46 short = 'F',
47 long,
48 action = ArgAction::Append,
49 help_heading = heading::FEATURE_SELECTION,
50 )]
51 #[cfg_attr(feature = "serde", serde(default))]
52 pub features: Vec<String>,
53
54 #[arg(long, help_heading = heading::FEATURE_SELECTION)]
56 #[cfg_attr(feature = "serde", serde(default))]
57 pub all_features: bool,
58
59 #[arg(long, help_heading = heading::FEATURE_SELECTION)]
61 #[cfg_attr(feature = "serde", serde(default))]
62 pub no_default_features: bool,
63
64 #[arg(
66 long,
67 value_name = "TRIPLE",
68 num_args = 0..=1,
69 default_missing_value = "",
70 action = ArgAction::Append,
71 help_heading = heading::COMPILATION_OPTIONS,
72 )]
73 #[cfg_attr(feature = "serde", serde(default))]
74 pub target: Vec<String>,
75
76 #[arg(
78 long,
79 value_name = "DIRECTORY",
80 help_heading = heading::COMPILATION_OPTIONS,
81 )]
82 #[cfg_attr(feature = "serde", serde(default))]
83 pub target_dir: Option<PathBuf>,
84
85 #[arg(
87 long,
88 value_name = "FMT",
89 action = ArgAction::Append,
90 value_delimiter = ',',
91 ignore_case = true,
92 value_parser = [
93 "human",
94 "short",
95 "json",
96 "json-diagnostic-short",
97 "json-diagnostic-rendered-ansi",
98 "json-render-diagnostics",
99 ],
100 )]
101 #[cfg_attr(feature = "serde", serde(default))]
102 pub message_format: Vec<String>,
103
104 #[arg(short = 'v', long, action = ArgAction::Count)]
106 #[cfg_attr(feature = "serde", serde(default))]
107 pub verbose: u8,
108
109 #[arg(long, value_name = "WHEN", value_parser = ["auto", "always", "never"])]
111 #[cfg_attr(feature = "serde", serde(default))]
112 pub color: Option<String>,
113
114 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
116 #[cfg_attr(feature = "serde", serde(default))]
117 pub frozen: bool,
118
119 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
121 #[cfg_attr(feature = "serde", serde(default))]
122 pub locked: bool,
123
124 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
126 #[cfg_attr(feature = "serde", serde(default))]
127 pub offline: bool,
128
129 #[arg(long, value_name = "KEY=VALUE|PATH", action = ArgAction::Append)]
131 #[cfg_attr(feature = "serde", serde(default))]
132 pub config: Vec<String>,
133
134 #[arg(short = 'Z', value_name = "FLAG", action = ArgAction::Append)]
136 #[cfg_attr(feature = "serde", serde(default))]
137 pub unstable_flags: Vec<String>,
138
139 #[arg(long, help_heading = heading::COMPILATION_OPTIONS)]
141 #[cfg_attr(feature = "serde", serde(default))]
142 pub timings: bool,
143}
144
145impl CommonOptions {
146 pub fn apply(&self, cmd: &mut Command) {
148 if self.quiet {
149 cmd.arg("--quiet");
150 }
151 if let Some(jobs) = self.jobs {
152 cmd.arg("--jobs").arg(jobs.to_string());
153 }
154 if self.keep_going {
155 cmd.arg("--keep-going");
156 }
157 if let Some(profile) = self.profile.as_ref() {
158 cmd.arg("--profile").arg(profile);
159 }
160 for feature in &self.features {
161 cmd.arg("--features").arg(feature);
162 }
163 if self.all_features {
164 cmd.arg("--all-features");
165 }
166 if self.no_default_features {
167 cmd.arg("--no-default-features");
168 }
169
170 let rust_targets = self
173 .target
174 .iter()
175 .map(|target| target.split_once('.').map(|(t, _)| t).unwrap_or(target))
176 .collect::<Vec<&str>>();
177 rust_targets.iter().for_each(|target| {
178 cmd.arg("--target");
179 if !target.is_empty() {
180 cmd.arg(target);
181 }
182 });
183
184 if let Some(dir) = self.target_dir.as_ref() {
185 cmd.arg("--target-dir").arg(dir);
186 }
187 for fmt in &self.message_format {
188 cmd.arg("--message-format").arg(fmt);
189 }
190 if self.verbose > 0 {
191 cmd.arg(format!("-{}", "v".repeat(self.verbose.into())));
192 }
193 if let Some(color) = self.color.as_ref() {
194 cmd.arg("--color").arg(color);
195 }
196 if self.frozen {
197 cmd.arg("--frozen");
198 }
199 if self.locked {
200 cmd.arg("--locked");
201 }
202 if self.offline {
203 cmd.arg("--offline");
204 }
205 for config in &self.config {
206 cmd.arg("--config").arg(config);
207 }
208 for flag in &self.unstable_flags {
209 cmd.arg("-Z").arg(flag);
210 }
211 if self.timings {
212 cmd.arg("--timings");
213 }
214 }
215
216 pub(crate) fn cargo_command() -> Command {
217 let cargo = match std::env::var_os("CARGO") {
218 Some(cargo) => cargo.into(),
219 None => PathBuf::from("cargo"),
220 };
221 let mut cmd = Command::new(cargo);
222 cmd.env_remove("CARGO");
223 cmd
224 }
225}