beet_cli/commands/
cargo_cmd.rs

1use anyhow::Result;
2use clap::Parser;
3use std::path::PathBuf;
4use std::process::Command;
5
6/// Verbatim clone of cargo run args
7#[derive(Debug, Clone, Parser)]
8#[command(name = "cargo-cmd")]
9pub struct CargoCmd {
10	/// Error format
11	#[arg(long, default_value = "build")]
12	pub cargo_cmd: String,
13	#[arg(long)]
14	pub message_format: Option<String>,
15	/// Use verbose output (-vv very verbose/build.rs output)
16	#[arg(short = 'v', long, action = clap::ArgAction::Count)]
17	pub verbose: u8,
18	/// Do not print cargo log messages
19	#[arg(short, long)]
20	pub quiet: bool,
21	/// Coloring: auto, always, never
22	#[arg(long)]
23	pub color: Option<String>,
24	/// Override a configuration value
25	pub config: Option<String>,
26	/// Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
27	#[arg(short = 'Z', long)]
28	pub z: Option<String>,
29	/// Package with the target to run
30	#[arg(short = 'p', long = "package")]
31	pub package: Option<String>,
32	/// Name of the bin target to run
33	#[arg(long)]
34	pub bin: Option<String>,
35	/// Name of the example target to run
36	#[arg(long)]
37	pub example: Option<String>,
38	/// Space or comma separated list of features to activate
39	#[arg(short = 'F', long = "features")]
40	pub features: Option<String>,
41	/// Activate all available features
42	#[arg(long)]
43	pub all_features: bool,
44	/// Do not activate the `default` feature
45	#[arg(long)]
46	pub no_default_features: bool,
47	/// Number of parallel jobs, defaults to # of CPUs.
48	#[arg(short = 'j', long)]
49	pub jobs: Option<String>,
50	/// Do not abort the build as soon as there is an error
51	#[arg(long)]
52	pub keep_going: bool,
53	/// Build artifacts in release mode, with optimizations
54	#[arg(long)]
55	pub release: bool,
56	/// Build artifacts with the specified profile
57	#[arg(long)]
58	pub profile: Option<String>,
59	/// Build for the target triple
60	#[arg(long)]
61	pub target: Option<String>,
62	/// Directory for all generated artifacts
63	#[arg(long)]
64	pub target_dir: Option<String>,
65	/// Output build graph in JSON (unstable)
66	#[arg(long)]
67	pub unit_graph: bool,
68	/// Timing output formats (unstable) (comma separated): html, json
69	#[arg(long)]
70	pub timings: Option<String>,
71	/// Path to Cargo.toml
72	#[arg(long)]
73	pub manifest_path: Option<String>,
74	/// Path to Cargo.lock (unstable)
75	#[arg(long)]
76	pub lockfile_path: Option<String>,
77	/// Ignore `rust-version` specification in packages
78	#[arg(long)]
79	pub ignore_rust_version: bool,
80	/// Assert that `Cargo.lock` will remain unchanged
81	#[arg(long)]
82	pub locked: bool,
83	/// Run without accessing the network
84	#[arg(long)]
85	pub offline: bool,
86	/// Equivalent to specifying both --locked and --offline
87	#[arg(long)]
88	pub frozen: bool,
89	/// Any additional arguments passed to cargo
90	#[arg(trailing_var_arg = true)]
91	pub args: Vec<String>,
92}
93
94impl Default for CargoCmd {
95	fn default() -> Self { Self::parse_from(&[""]) }
96}
97
98impl CargoCmd {
99	/// Best effort attempt to retrieve the path to the executable.
100	/// In the case of a wasm target, the path will have a `.wasm` extension.
101	pub fn exe_path(&self) -> PathBuf {
102		let target_dir = std::env::var("CARGO_TARGET_DIR")
103			.unwrap_or_else(|_| "target".to_string());
104		let mut path = PathBuf::from(target_dir);
105
106		if let Some(target) = &self.target {
107			path.push(target);
108		}
109
110		if self.release {
111			path.push("release");
112		} else {
113			path.push("debug");
114		}
115
116		if let Some(example) = &self.example {
117			path.push("examples");
118			path.push(example);
119		// package examples are not nested under package name
120		} else if let Some(pkg) = &self.package {
121			path.push(pkg);
122		}
123		if let Some(bin) = &self.bin {
124			path.push(bin);
125		}
126
127		if let Some("wasm32-unknown-unknown") = self.target.as_deref() {
128			path.set_extension("wasm");
129		}
130
131		path
132	}
133
134	pub fn spawn(&self) -> Result<()> {
135		let CargoCmd {
136			cargo_cmd,
137			args,
138			message_format,
139			verbose,
140			quiet,
141			color,
142			config,
143			z,
144			package,
145			bin,
146			example,
147			features,
148			all_features,
149			no_default_features,
150			jobs,
151			keep_going,
152			release,
153			profile,
154			target,
155			target_dir,
156			unit_graph,
157			timings,
158			manifest_path,
159			lockfile_path,
160			ignore_rust_version,
161			locked,
162			offline,
163			frozen,
164		} = self;
165		let mut cmd = Command::new("cargo");
166		cmd.arg(cargo_cmd);
167
168
169		if let Some(format) = message_format {
170			cmd.arg("--message-format").arg(format);
171		}
172
173		for _ in 0..*verbose {
174			cmd.arg("-v");
175		}
176
177		if *quiet {
178			cmd.arg("--quiet");
179		}
180
181		if let Some(c) = color {
182			cmd.arg("--color").arg(c);
183		}
184
185		if let Some(cfg) = config {
186			cmd.arg("--config").arg(cfg);
187		}
188
189		if let Some(z_flag) = z {
190			cmd.arg("-Z").arg(z_flag);
191		}
192
193		if let Some(pkg) = package {
194			cmd.arg("--package").arg(pkg);
195		}
196
197		if let Some(b) = bin {
198			cmd.arg("--bin").arg(b);
199		}
200
201		if let Some(ex) = example {
202			cmd.arg("--example").arg(ex);
203		}
204
205		if let Some(feat) = features {
206			cmd.arg("--features").arg(feat);
207		}
208
209		if *all_features {
210			cmd.arg("--all-features");
211		}
212
213		if *no_default_features {
214			cmd.arg("--no-default-features");
215		}
216
217		if let Some(j) = jobs {
218			cmd.arg("-j").arg(j);
219		}
220
221		if *keep_going {
222			cmd.arg("--keep-going");
223		}
224
225		if *release {
226			cmd.arg("--release");
227		}
228
229		if let Some(prof) = profile {
230			cmd.arg("--profile").arg(prof);
231		}
232
233		if let Some(tgt) = target {
234			cmd.arg("--target").arg(tgt);
235		}
236
237		if let Some(dir) = target_dir {
238			cmd.arg("--target-dir").arg(dir);
239		}
240
241		if *unit_graph {
242			cmd.arg("--unit-graph");
243		}
244
245		if let Some(tim) = timings {
246			cmd.arg("--timings").arg(tim);
247		}
248
249		if let Some(path) = manifest_path {
250			cmd.arg("--manifest-path").arg(path);
251		}
252
253		if let Some(lock) = lockfile_path {
254			cmd.arg("--lockfile-path").arg(lock);
255		}
256
257		if *ignore_rust_version {
258			cmd.arg("--ignore-rust-version");
259		}
260
261		if *locked {
262			cmd.arg("--locked");
263		}
264
265		if *offline {
266			cmd.arg("--offline");
267		}
268
269		if *frozen {
270			cmd.arg("--frozen");
271		}
272
273		cmd.args(args);
274
275		cmd.status()?.exit_ok()?;
276		Ok(())
277	}
278}
279
280//cargo build -p beet_site --message-format=json | jq -r 'select(.reason == "compiler-artifact" and .target.kind == ["bin"]) | .filenames[]'