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