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.clone().unwrap());
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());
93 self.args.push(target.name.clone());
94 self.args.push("--manifest-path".into());
95 self.args.push(
96 target
97 .manifest_path
98 .clone()
99 .to_str()
100 .unwrap_or_default()
101 .to_owned(),
102 );
103 }
104 TargetKind::Binary | TargetKind::ExtendedBinary => {
105 self.args.push("run".into());
106 self.args.push("--bin".into());
107 self.args.push(target.name.clone());
108 self.args.push("--manifest-path".into());
109 self.args.push(
110 target
111 .manifest_path
112 .clone()
113 .to_str()
114 .unwrap_or_default()
115 .to_owned(),
116 );
117 }
118 TargetKind::Manifest => {
119 self.suppressed_flags.insert("quiet".to_string());
120 self.args.push("run".into());
121 self.args.push("--manifest-path".into());
122 self.args.push(
123 target
124 .manifest_path
125 .clone()
126 .to_str()
127 .unwrap_or_default()
128 .to_owned(),
129 );
130 }
131 TargetKind::ManifestTauriExample => {
132 self.suppressed_flags.insert("quiet".to_string());
133 self.args.push("run".into());
134 self.args.push("--example".into());
135 self.args.push(target.name.clone());
136 self.args.push("--manifest-path".into());
137 self.args.push(
138 target
139 .manifest_path
140 .clone()
141 .to_str()
142 .unwrap_or_default()
143 .to_owned(),
144 );
145 }
146 TargetKind::ManifestTauri => {
147 self.suppressed_flags.insert("quiet".to_string());
148 let has_tauri_conf = |dir: &Path| -> bool { dir.join("tauri.conf.json").exists() };
150
151 let candidate_dir_opt = match &target.origin {
153 Some(TargetOrigin::SingleFile(path))
154 | Some(TargetOrigin::DefaultBinary(path)) => path.parent(),
155 _ => None,
156 };
157
158 if let Some(candidate_dir) = candidate_dir_opt {
159 if has_tauri_conf(candidate_dir) {
160 println!("Using candidate directory: {}", candidate_dir.display());
161 self.execution_dir = Some(candidate_dir.to_path_buf());
162 } else if let Some(manifest_parent) = target.manifest_path.parent() {
163 if has_tauri_conf(manifest_parent) {
164 println!("Using manifest parent: {}", manifest_parent.display());
165 self.execution_dir = Some(manifest_parent.to_path_buf());
166 } else if let Some(grandparent) = manifest_parent.parent() {
167 if has_tauri_conf(grandparent) {
168 println!("Using manifest grandparent: {}", grandparent.display());
169 self.execution_dir = Some(grandparent.to_path_buf());
170 } else {
171 println!("No tauri.conf.json found in candidate, manifest parent, or grandparent; defaulting to manifest parent: {}", manifest_parent.display());
172 self.execution_dir = Some(manifest_parent.to_path_buf());
173 }
174 } else {
175 println!("No grandparent for manifest; defaulting to candidate directory: {}", candidate_dir.display());
176 self.execution_dir = Some(candidate_dir.to_path_buf());
177 }
178 } else {
179 println!(
180 "No manifest parent found for: {}",
181 target.manifest_path.display()
182 );
183 }
184 } else if let Some(manifest_parent) = target.manifest_path.parent() {
185 if has_tauri_conf(manifest_parent) {
186 println!("Using manifest parent: {}", manifest_parent.display());
187 self.execution_dir = Some(manifest_parent.to_path_buf());
188 } else if let Some(grandparent) = manifest_parent.parent() {
189 if has_tauri_conf(grandparent) {
190 println!("Using manifest grandparent: {}", grandparent.display());
191 self.execution_dir = Some(grandparent.to_path_buf());
192 } else {
193 println!(
194 "No tauri.conf.json found; defaulting to manifest parent: {}",
195 manifest_parent.display()
196 );
197 self.execution_dir = Some(manifest_parent.to_path_buf());
198 }
199 }
200 } else {
201 println!(
202 "No manifest parent found for: {}",
203 target.manifest_path.display()
204 );
205 }
206 self.args.push("tauri".into());
207 self.args.push("dev".into());
208 }
209 TargetKind::ManifestDioxus => {
210 let exe_path = match which("dx") {
211 Ok(path) => path,
212 Err(err) => {
213 eprintln!("Error: 'dx' not found in PATH: {}", err);
214 return self;
215 }
216 };
217 if let Some(manifest_parent) = target.manifest_path.parent() {
220 println!("Manifest path: {}", target.manifest_path.display());
221 println!(
222 "Execution directory (same as manifest folder): {}",
223 manifest_parent.display()
224 );
225 self.execution_dir = Some(manifest_parent.to_path_buf());
226 } else {
227 println!(
228 "No manifest parent found for: {}",
229 target.manifest_path.display()
230 );
231 }
232 self.alternate_cmd = Some(exe_path.as_os_str().to_string_lossy().to_string());
233 self.args.push("serve".into());
234 self = self.with_required_features(&target.manifest_path, target);
235 }
236 TargetKind::ManifestDioxusExample => {
237 let exe_path = match which("dx") {
238 Ok(path) => path,
239 Err(err) => {
240 eprintln!("Error: 'dx' not found in PATH: {}", err);
241 return self;
242 }
243 };
244 if let Some(manifest_parent) = target.manifest_path.parent() {
247 println!("Manifest path: {}", target.manifest_path.display());
248 println!(
249 "Execution directory (same as manifest folder): {}",
250 manifest_parent.display()
251 );
252 self.execution_dir = Some(manifest_parent.to_path_buf());
253 } else {
254 println!(
255 "No manifest parent found for: {}",
256 target.manifest_path.display()
257 );
258 }
259 self.alternate_cmd = Some(exe_path.as_os_str().to_string_lossy().to_string());
260 self.args.push("serve".into());
261 self.args.push("--example".into());
262 self.args.push(target.name.clone());
263 self = self.with_required_features(&target.manifest_path, target);
264 }
265 }
266 self
267 }
268
269 pub fn with_cli(mut self, cli: &crate::Cli) -> Self {
271 if cli.quiet && !self.suppressed_flags.contains("quiet") {
272 if let Some(pos) = self.args.iter().position(|arg| arg == "run") {
274 self.args.insert(pos + 1, "--quiet".into());
275 } else {
276 self.args.push("--quiet".into());
277 }
278 }
279 if cli.release {
280 if let Some(pos) = self.args.iter().position(|arg| arg == "run") {
283 self.args.insert(pos + 1, "--release".into());
284 } else {
285 self.args.push("--release".into());
287 }
288 }
289 if !cli.extra.is_empty() {
291 self.args.push("--".into());
292 self.args.extend(cli.extra.iter().cloned());
293 }
294 self
295 }
296 pub fn with_required_features(mut self, manifest: &PathBuf, target: &CargoTarget) -> Self {
300 if let Some(features) = crate::e_manifest::get_required_features_from_manifest(
301 manifest,
302 &target.kind,
303 &target.name,
304 ) {
305 self.args.push("--features".to_string());
306 self.args.push(features);
307 }
308 self
309 }
310
311 pub fn with_extra_args(mut self, extra: &[String]) -> Self {
313 if !extra.is_empty() {
314 self.args.push("--".into());
316 self.args.extend(extra.iter().cloned());
317 }
318 self
319 }
320
321 pub fn build(self) -> Vec<String> {
323 self.args
324 }
325
326 pub fn build_command(self) -> Command {
328 let mut cmd = if let Some(alternate) = self.alternate_cmd {
329 Command::new(alternate)
330 } else {
331 Command::new("cargo")
332 };
333 cmd.args(self.args);
334 if let Some(dir) = self.execution_dir {
335 cmd.current_dir(dir);
336 }
337 cmd
338 }
339}
340
341#[cfg(test)]
343mod tests {
344 use crate::e_target::TargetOrigin;
345
346 use super::*;
347
348 #[test]
349 fn test_command_builder_example() {
350 let target = CargoTarget {
351 name: "my_example".to_string(),
352 display_name: "My Example".to_string(),
353 manifest_path: "Cargo.toml".into(),
354 kind: TargetKind::Example,
355 extended: true,
356 toml_specified: false,
357 origin: Some(TargetOrigin::SingleFile(PathBuf::from(
358 "examples/my_example.rs",
359 ))),
360 };
361
362 let extra_args = vec!["--flag".to_string(), "value".to_string()];
363
364 let args = CargoCommandBuilder::new()
365 .with_target(&target)
366 .with_extra_args(&extra_args)
367 .build();
368
369 assert!(args.contains(&"run".to_string()));
372 assert!(args.contains(&"--example".to_string()));
373 assert!(args.contains(&"my_example".to_string()));
374 assert!(args.contains(&"--manifest-path".to_string()));
375 assert!(args.contains(&"Cargo.toml".to_string()));
376 assert!(args.contains(&"--".to_string()));
377 assert!(args.contains(&"--flag".to_string()));
378 assert!(args.contains(&"value".to_string()));
379 }
380}