cargo_e/
e_command_builder.rs1use std::collections::HashSet;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4use which::which;
5
6use crate::e_target::{CargoTarget, TargetKind, TargetOrigin};
7
8#[derive(Clone)]
10pub struct CargoCommandBuilder {
11 pub args: Vec<String>,
12 pub alternate_cmd: Option<String>,
13 pub execution_dir: Option<PathBuf>,
14 pub suppressed_flags: HashSet<String>,
15}
16impl Default for CargoCommandBuilder {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21impl CargoCommandBuilder {
22 pub fn new() -> Self {
24 CargoCommandBuilder {
25 args: Vec::new(),
26 alternate_cmd: None,
27 execution_dir: None,
28 suppressed_flags: HashSet::new(),
29 }
30 }
31
32 pub fn with_target(mut self, target: &CargoTarget) -> Self {
74 println!("Target origin: {:?}", target.origin);
75 match target.kind {
76 TargetKind::Unknown => {
77 return self;
78 }
79 TargetKind::Bench => {
80 self.alternate_cmd = Some("bench".to_string());
82 self.args.push(target.name.clone());
83 }
84 TargetKind::Test => {
85 self.args.push("test".into());
86 self.args.push(target.name.clone());
88 }
89 TargetKind::Example | TargetKind::ExtendedExample => {
90 self.args.push("run".into());
91 self.args.push("--example".into());
92 self.args.push(target.name.clone());
93 self.args.push("--manifest-path".into());
94 self.args.push(
95 target
96 .manifest_path
97 .clone()
98 .to_str()
99 .unwrap_or_default()
100 .to_owned(),
101 );
102 }
103 TargetKind::Binary | TargetKind::ExtendedBinary => {
104 self.args.push("run".into());
105 self.args.push("--bin".into());
106 self.args.push(target.name.clone());
107 self.args.push("--manifest-path".into());
108 self.args.push(
109 target
110 .manifest_path
111 .clone()
112 .to_str()
113 .unwrap_or_default()
114 .to_owned(),
115 );
116 }
117 TargetKind::Manifest => {
118 self.suppressed_flags.insert("quiet".to_string());
119 self.args.push("run".into());
120 self.args.push("--manifest-path".into());
121 self.args.push(
122 target
123 .manifest_path
124 .clone()
125 .to_str()
126 .unwrap_or_default()
127 .to_owned(),
128 );
129 }
130 TargetKind::ManifestTauriExample => {
131 self.suppressed_flags.insert("quiet".to_string());
132 self.args.push("run".into());
133 self.args.push("--example".into());
134 self.args.push(target.name.clone());
135 self.args.push("--manifest-path".into());
136 self.args.push(
137 target
138 .manifest_path
139 .clone()
140 .to_str()
141 .unwrap_or_default()
142 .to_owned(),
143 );
144 }
145 TargetKind::ManifestTauri => {
146 self.suppressed_flags.insert("quiet".to_string());
147 let has_tauri_conf = |dir: &Path| -> bool { dir.join("tauri.conf.json").exists() };
149
150 let candidate_dir_opt = match &target.origin {
152 Some(TargetOrigin::SingleFile(path))
153 | Some(TargetOrigin::DefaultBinary(path)) => path.parent(),
154 _ => None,
155 };
156
157 if let Some(candidate_dir) = candidate_dir_opt {
158 if has_tauri_conf(candidate_dir) {
159 println!("Using candidate directory: {}", candidate_dir.display());
160 self.execution_dir = Some(candidate_dir.to_path_buf());
161 } else if let Some(manifest_parent) = target.manifest_path.parent() {
162 if has_tauri_conf(manifest_parent) {
163 println!("Using manifest parent: {}", manifest_parent.display());
164 self.execution_dir = Some(manifest_parent.to_path_buf());
165 } else if let Some(grandparent) = manifest_parent.parent() {
166 if has_tauri_conf(grandparent) {
167 println!("Using manifest grandparent: {}", grandparent.display());
168 self.execution_dir = Some(grandparent.to_path_buf());
169 } else {
170 println!("No tauri.conf.json found in candidate, manifest parent, or grandparent; defaulting to manifest parent: {}", manifest_parent.display());
171 self.execution_dir = Some(manifest_parent.to_path_buf());
172 }
173 } else {
174 println!("No grandparent for manifest; defaulting to candidate directory: {}", candidate_dir.display());
175 self.execution_dir = Some(candidate_dir.to_path_buf());
176 }
177 } else {
178 println!(
179 "No manifest parent found for: {}",
180 target.manifest_path.display()
181 );
182 }
183 } else if let Some(manifest_parent) = target.manifest_path.parent() {
184 if has_tauri_conf(manifest_parent) {
185 println!("Using manifest parent: {}", manifest_parent.display());
186 self.execution_dir = Some(manifest_parent.to_path_buf());
187 } else if let Some(grandparent) = manifest_parent.parent() {
188 if has_tauri_conf(grandparent) {
189 println!("Using manifest grandparent: {}", grandparent.display());
190 self.execution_dir = Some(grandparent.to_path_buf());
191 } else {
192 println!(
193 "No tauri.conf.json found; defaulting to manifest parent: {}",
194 manifest_parent.display()
195 );
196 self.execution_dir = Some(manifest_parent.to_path_buf());
197 }
198 }
199 } else {
200 println!(
201 "No manifest parent found for: {}",
202 target.manifest_path.display()
203 );
204 }
205 self.args.push("tauri".into());
206 self.args.push("dev".into());
207 }
208 TargetKind::ManifestDioxus => {
209 let exe_path = match which("dx") {
210 Ok(path) => path,
211 Err(err) => {
212 eprintln!("Error: 'dx' not found in PATH: {}", err);
213 return self;
214 }
215 };
216 if let Some(manifest_parent) = target.manifest_path.parent() {
219 println!("Manifest path: {}", target.manifest_path.display());
220 println!(
221 "Execution directory (same as manifest folder): {}",
222 manifest_parent.display()
223 );
224 self.execution_dir = Some(manifest_parent.to_path_buf());
225 } else {
226 println!(
227 "No manifest parent found for: {}",
228 target.manifest_path.display()
229 );
230 }
231 self.alternate_cmd = Some(exe_path.as_os_str().to_string_lossy().to_string());
232 self.args.push("serve".into());
233 self = self.with_required_features(&target.manifest_path, target);
234 }
235 TargetKind::ManifestDioxusExample => {
236 let exe_path = match which("dx") {
237 Ok(path) => path,
238 Err(err) => {
239 eprintln!("Error: 'dx' not found in PATH: {}", err);
240 return self;
241 }
242 };
243 if let Some(manifest_parent) = target.manifest_path.parent() {
246 println!("Manifest path: {}", target.manifest_path.display());
247 println!(
248 "Execution directory (same as manifest folder): {}",
249 manifest_parent.display()
250 );
251 self.execution_dir = Some(manifest_parent.to_path_buf());
252 } else {
253 println!(
254 "No manifest parent found for: {}",
255 target.manifest_path.display()
256 );
257 }
258 self.alternate_cmd = Some(exe_path.as_os_str().to_string_lossy().to_string());
259 self.args.push("serve".into());
260 self.args.push("--example".into());
261 self.args.push(target.name.clone());
262 self = self.with_required_features(&target.manifest_path, target);
263 }
264 }
265 self
266 }
267
268 pub fn with_cli(mut self, cli: &crate::Cli) -> Self {
270 if cli.quiet && !self.suppressed_flags.contains("quiet") {
271 if let Some(pos) = self.args.iter().position(|arg| arg == "run") {
273 self.args.insert(pos + 1, "--quiet".into());
274 } else {
275 self.args.push("--quiet".into());
276 }
277 }
278 if cli.release {
279 if let Some(pos) = self.args.iter().position(|arg| arg == "run") {
282 self.args.insert(pos + 1, "--release".into());
283 } else {
284 self.args.push("--release".into());
286 }
287 }
288 if !cli.extra.is_empty() {
290 self.args.push("--".into());
291 self.args.extend(cli.extra.iter().cloned());
292 }
293 self
294 }
295 pub fn with_required_features(mut self, manifest: &PathBuf, target: &CargoTarget) -> Self {
299 if let Some(features) = crate::e_manifest::get_required_features_from_manifest(
300 manifest,
301 &target.kind,
302 &target.name,
303 ) {
304 self.args.push("--features".to_string());
305 self.args.push(features);
306 }
307 self
308 }
309
310 pub fn with_extra_args(mut self, extra: &[String]) -> Self {
312 if !extra.is_empty() {
313 self.args.push("--".into());
315 self.args.extend(extra.iter().cloned());
316 }
317 self
318 }
319
320 pub fn build(self) -> Vec<String> {
322 self.args
323 }
324
325 pub fn build_command(self) -> Command {
327 let mut cmd = if let Some(alternate) = self.alternate_cmd {
328 Command::new(alternate)
329 } else {
330 Command::new("cargo")
331 };
332 cmd.args(self.args);
333 if let Some(dir) = self.execution_dir {
334 cmd.current_dir(dir);
335 }
336 cmd
337 }
338}
339
340#[cfg(test)]
342mod tests {
343 use crate::e_target::TargetOrigin;
344
345 use super::*;
346
347 #[test]
348 fn test_command_builder_example() {
349 let target = CargoTarget {
350 name: "my_example".to_string(),
351 display_name: "My Example".to_string(),
352 manifest_path: "Cargo.toml".into(),
353 kind: TargetKind::Example,
354 extended: true,
355 origin: Some(TargetOrigin::SingleFile(PathBuf::from(
356 "examples/my_example.rs",
357 ))),
358 };
359
360 let extra_args = vec!["--flag".to_string(), "value".to_string()];
361
362 let args = CargoCommandBuilder::new()
363 .with_target(&target)
364 .with_extra_args(&extra_args)
365 .build();
366
367 assert!(args.contains(&"run".to_string()));
370 assert!(args.contains(&"--example".to_string()));
371 assert!(args.contains(&"my_example".to_string()));
372 assert!(args.contains(&"--manifest-path".to_string()));
373 assert!(args.contains(&"Cargo.toml".to_string()));
374 assert!(args.contains(&"--".to_string()));
375 assert!(args.contains(&"--flag".to_string()));
376 assert!(args.contains(&"value".to_string()));
377 }
378}