cargo_e/e_runner.rs
1use crate::{e_target::TargetOrigin, prelude::*};
2// #[cfg(not(feature = "equivalent"))]
3// use ctrlc;
4use once_cell::sync::Lazy;
5use which::which;
6
7// Global shared container for the currently running child process.
8pub static GLOBAL_CHILD: Lazy<Arc<Mutex<Option<Child>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
9
10/// Registers a global Ctrl+C handler once.
11/// The handler checks GLOBAL_CHILD and kills the child process if present.
12pub fn register_ctrlc_handler() -> Result<(), Box<dyn Error>> {
13 ctrlc::set_handler(move || {
14 let mut child_lock = GLOBAL_CHILD.lock().unwrap();
15 if let Some(child) = child_lock.as_mut() {
16 eprintln!("Ctrl+C pressed, terminating running child process...");
17 let _ = child.kill();
18 } else {
19 eprintln!("Ctrl+C pressed, no child process running. Exiting nicely.");
20 exit(0);
21 }
22 })?;
23 Ok(())
24}
25
26use crate::e_target::CargoTarget;
27use std::process::Command; // Adjust the import based on your project structure
28
29/// Asynchronously launches the GenAI summarization example for the given target.
30/// It builds the command using the target's manifest path as the "origin" argument.
31pub async fn open_ai_summarize_for_target(target: &CargoTarget) {
32 // Extract the origin path from the target (e.g. the manifest path).
33 let origin_path = match &target.origin {
34 Some(TargetOrigin::SingleFile(path)) | Some(TargetOrigin::DefaultBinary(path)) => path,
35 _ => return (),
36 };
37
38 let exe_path = match which("cargoe_ai_summarize") {
39 Ok(path) => path,
40 Err(err) => {
41 eprintln!("Error: 'cargoe_ai_summarize' not found in PATH: {}", err);
42 return;
43 }
44 };
45 // Build the command based on the platform.
46 // let mut cmd = if cfg!(target_os = "windows") {
47 // let command_str = format!(
48 // "e_ai_summarize --streaming --stdin {}",
49 // origin_path.as_os_str().to_string_lossy()
50 // );
51 // println!("Running command: {}", command_str);
52 // let mut command = Command::new("cmd");
53 // command.args(["/C", &command_str]);
54 // command
55 // } else {
56 let mut cmd = Command::new(exe_path);
57 cmd.arg("--streaming");
58 cmd.arg("--stdin");
59 cmd.arg(origin_path);
60 // command
61 // };
62
63 cmd.stdin(Stdio::inherit())
64 .stdout(Stdio::inherit())
65 .stderr(Stdio::inherit());
66
67 // Spawn the command and wait for it to finish.
68 let child = cmd.spawn();
69 let status = child
70 .expect("Failed to spawn command")
71 .wait()
72 .expect("Failed to wait for command");
73
74 if !status.success() {
75 eprintln!("Command exited with status: {}", status);
76 }
77
78 // // Build the command to run the example.
79 // let output = if cfg!(target_os = "windows") {
80 // let command_str = format!("e_ai_summarize --stdin {}", origin_path.as_os_str().to_string_lossy());
81 // println!("Running command: {}", command_str);
82 // Command::new("cmd")
83 // .args([
84 // "/C",
85 // command_str.as_str(),
86 // ])
87 // .output()
88 // } else {
89 // Command::new("e_ai_summarize")
90 // .args([origin_path])
91 // .output()
92 // };
93
94 // // Handle the output from the command.
95 // match output {
96 // Ok(output) if output.status.success() => {
97 // // The summarization example ran successfully.
98 // println!("----
99 // {}", String::from_utf8_lossy(&output.stdout));
100 // }
101 // Ok(output) => {
102 // let msg = format!(
103 // "Error running summarization example:\nstdout: {}\nstderr: {}",
104 // String::from_utf8_lossy(&output.stdout),
105 // String::from_utf8_lossy(&output.stderr)
106 // );
107 // error!("{}", msg);
108 // }
109 // Err(e) => {
110 // let msg = format!("Failed to execute summarization command: {}", e);
111 // error!("{}", msg);
112 // }
113 // }
114}
115
116/// In "equivalent" mode, behave exactly like "cargo run --example <name>"
117#[cfg(feature = "equivalent")]
118pub fn run_equivalent_example(
119 cli: &crate::Cli,
120) -> Result<std::process::ExitStatus, Box<dyn Error>> {
121 // In "equivalent" mode, behave exactly like "cargo run --example <name>"
122 let mut cmd = Command::new("cargo");
123 cmd.args([
124 "run",
125 "--example",
126 cli.explicit_example.as_deref().unwrap_or(""),
127 ]);
128 if !cli.extra.is_empty() {
129 cmd.arg("--").args(cli.extra.clone());
130 }
131 // Inherit the standard input (as well as stdout/stderr) so that input is passed through.
132 use std::process::Stdio;
133 cmd.stdin(Stdio::inherit())
134 .stdout(Stdio::inherit())
135 .stderr(Stdio::inherit());
136
137 let status = cmd.status()?;
138 std::process::exit(status.code().unwrap_or(1));
139}
140
141/// Runs the given example (or binary) target.
142pub fn run_example(
143 cli: &crate::Cli,
144 target: &crate::e_target::CargoTarget,
145) -> anyhow::Result<std::process::ExitStatus> {
146 // Retrieve the current package name at compile time.
147 let current_bin = env!("CARGO_PKG_NAME");
148
149 // Avoid running our own binary.
150 if target.kind == crate::e_target::TargetKind::Binary && target.name == current_bin {
151 return Err(anyhow::anyhow!(
152 "Skipping automatic run: {} is the same as the running binary",
153 target.name
154 ));
155 }
156
157 // Build the command using the CargoCommandBuilder.
158 let mut builder = crate::e_command_builder::CargoCommandBuilder::new()
159 .with_target(target)
160 .with_required_features(&target.manifest_path, target)
161 .with_cli(cli);
162
163 if !cli.extra.is_empty() {
164 builder = builder.with_extra_args(&cli.extra);
165 }
166
167 // Build the command.
168 let mut cmd = builder.clone().build_command();
169
170 // Before spawning, determine the directory to run from.
171 // If a custom execution directory was set (e.g. for Tauri targets), that is used.
172 // Otherwise, if the target is extended, run from its parent directory.
173 if let Some(exec_dir) = builder.execution_dir {
174 cmd.current_dir(exec_dir);
175 } else if target.extended {
176 if let Some(dir) = target.manifest_path.parent() {
177 cmd.current_dir(dir);
178 }
179 }
180
181 // Print the full command for debugging.
182 let full_command = format!(
183 "{} {}",
184 cmd.get_program().to_string_lossy(),
185 cmd.get_args()
186 .map(|arg| arg.to_string_lossy())
187 .collect::<Vec<_>>()
188 .join(" ")
189 );
190 println!("Running: {}", full_command);
191
192 // Check if the manifest triggers the workspace error.
193 let maybe_backup = crate::e_manifest::maybe_patch_manifest_for_run(&target.manifest_path)?;
194
195 // Spawn the process.
196 let child = cmd.spawn()?;
197 {
198 let mut global = GLOBAL_CHILD.lock().unwrap();
199 *global = Some(child);
200 }
201 let status = {
202 let mut global = GLOBAL_CHILD.lock().unwrap();
203 if let Some(mut child) = global.take() {
204 child.wait()?
205 } else {
206 return Err(anyhow::anyhow!("Child process missing"));
207 }
208 };
209
210 // Restore the manifest if we patched it.
211 if let Some(original) = maybe_backup {
212 fs::write(&target.manifest_path, original)?;
213 }
214
215 Ok(status)
216}
217// /// Runs an example or binary target, applying a temporary manifest patch if a workspace error is detected.
218// /// This function uses the same idea as in the collection helpers: if the workspace error is found,
219// /// we patch the manifest, run the command, and then restore the manifest.
220// pub fn run_example(
221// target: &crate::e_target::CargoTarget,
222// extra_args: &[String],
223// ) -> Result<std::process::ExitStatus, Box<dyn Error>> {
224// // Retrieve the current package name (or binary name) at compile time.
225
226// use crate::e_target::TargetKind;
227
228// let current_bin = env!("CARGO_PKG_NAME");
229
230// // Avoid running our own binary if the target's name is the same.
231// if target.kind == TargetKind::Binary && target.name == current_bin {
232// return Err(format!(
233// "Skipping automatic run: {} is the same as the running binary",
234// target.name
235// )
236// .into());
237// }
238
239// let mut cmd = Command::new("cargo");
240// // Determine which manifest file is used.
241// let manifest_path: PathBuf;
242
243// match target.kind {
244// TargetKind::Bench => {
245// manifest_path = PathBuf::from(target.manifest_path.clone());
246// cmd.args([
247// "bench",
248// "--bench",
249// &target.name,
250// "--manifest-path",
251// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
252// ]);
253// }
254// TargetKind::Test => {
255// manifest_path = PathBuf::from(target.manifest_path.clone());
256// cmd.args([
257// "test",
258// "--test",
259// &target.name,
260// "--manifest-path",
261// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
262// ]);
263// }
264// TargetKind::Manifest => {
265// manifest_path = PathBuf::from(target.manifest_path.clone());
266// cmd.args([
267// "run",
268// "--release",
269// "--manifest-path",
270// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
271// "-p",
272// &target.name,
273// ]);
274// }
275// TargetKind::Example => {
276// if target.extended {
277// println!(
278// "Running extended example in folder: examples/{}",
279// target.name
280// );
281// // For extended examples, assume the manifest is inside the example folder.
282// manifest_path = PathBuf::from(format!("examples/{}/Cargo.toml", target.name));
283// cmd.arg("run")
284// .current_dir(format!("examples/{}", target.name));
285// } else {
286// manifest_path = PathBuf::from(crate::locate_manifest(false)?);
287// cmd.args([
288// "run",
289// "--release",
290// "--example",
291// &target.name,
292// "--manifest-path",
293// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
294// ]);
295// }
296// }
297// TargetKind::Binary => {
298// println!("Running binary: {}", target.name);
299// manifest_path = PathBuf::from(crate::locate_manifest(false)?);
300// cmd.args([
301// "run",
302// "--release",
303// "--bin",
304// &target.name,
305// "--manifest-path",
306// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
307// ]);
308// }
309// TargetKind::ExtendedBinary => {
310// println!("Running extended binary: {}", target.name);
311// manifest_path = PathBuf::from(crate::locate_manifest(false)?);
312// cmd.args([
313// "run",
314// "--release",
315// "--manifest-path",
316// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
317// "--bin",
318// &target.name,
319// ]);
320// }
321// TargetKind::ExtendedExample => {
322// println!("Running extended example: {}", target.name);
323// manifest_path = PathBuf::from(crate::locate_manifest(false)?);
324// cmd.args([
325// "run",
326// "--release",
327// "--manifest-path",
328// &target.manifest_path.to_str().unwrap_or_default().to_owned(),
329// "--example",
330// &target.name,
331// ]);
332// }
333// TargetKind::ManifestTauri => {
334// println!("Running tauri: {}", target.name);
335// // For a Tauri example, run `cargo tauri dev`
336// manifest_path = PathBuf::from(target.manifest_path.clone());
337// let manifest_dir = PathBuf::from(manifest_path.parent().expect("expected a parent"));
338// // Start a new command for tauri dev
339// cmd.arg("tauri").arg("dev").current_dir(manifest_dir); // run from the folder where Cargo.toml is located
340// }
341// TargetKind::ManifestDioxus => {
342// println!("Running dioxus: {}", target.name);
343// cmd = Command::new("dx");
344// // For a Tauri example, run `cargo tauri dev`
345// manifest_path = PathBuf::from(target.manifest_path.clone());
346// let manifest_dir = PathBuf::from(manifest_path.parent().expect("expected a parent"));
347// // Start a new command for tauri dev
348// cmd.arg("serve").current_dir(manifest_dir); // run from the folder where Cargo.toml is located
349// }
350// TargetKind::ManifestDioxusExample => {
351// println!("Running dioxus: {}", target.name);
352// cmd = Command::new("dx");
353// // For a Tauri example, run `cargo tauri dev`
354// manifest_path = PathBuf::from(target.manifest_path.clone());
355// let manifest_dir = PathBuf::from(manifest_path.parent().expect("expected a parent"));
356// // Start a new command for tauri dev
357// cmd.arg("serve")
358// .arg("--example")
359// .arg(&target.name)
360// .current_dir(manifest_dir); // run from the folder where Cargo.toml is located
361// }
362// }
363
364// // --- Add required-features support ---
365// // This call will search the provided manifest, and if it's a workspace,
366// // it will search workspace members for the target.
367// if let Some(features) = crate::e_manifest::get_required_features_from_manifest(
368// manifest_path.as_path(),
369// &target.kind,
370// &target.name,
371// ) {
372// cmd.args(&["--features", &features]);
373// }
374// // --- End required-features support ---
375
376// if !extra_args.is_empty() {
377// cmd.arg("--").args(extra_args);
378// }
379
380// let full_command = format!(
381// "{} {}",
382// cmd.get_program().to_string_lossy(),
383// cmd.get_args()
384// .map(|arg| arg.to_string_lossy())
385// .collect::<Vec<_>>()
386// .join(" ")
387// );
388// println!("Running: {}", full_command);
389
390// // Before spawning, check if the manifest triggers the workspace error.
391// // If so, patch it temporarily.
392// let maybe_backup = crate::e_manifest::maybe_patch_manifest_for_run(&manifest_path)?;
393
394// // Spawn the process.
395// let child = cmd.spawn()?;
396// {
397// let mut global = GLOBAL_CHILD.lock().unwrap();
398// *global = Some(child);
399// }
400// let status = {
401// let mut global = GLOBAL_CHILD.lock().unwrap();
402// if let Some(mut child) = global.take() {
403// child.wait()?
404// } else {
405// return Err("Child process missing".into());
406// }
407// };
408
409// // Restore the manifest if we patched it.
410// if let Some(original) = maybe_backup {
411// fs::write(&manifest_path, original)?;
412// }
413
414// // println!("Process exited with status: {:?}", status.code());
415// Ok(status)
416// }
417/// Helper function to spawn a cargo process.
418/// On Windows, this sets the CREATE_NEW_PROCESS_GROUP flag.
419pub fn spawn_cargo_process(args: &[&str]) -> Result<Child, Box<dyn Error>> {
420 #[cfg(windows)]
421 {
422 use std::os::windows::process::CommandExt;
423 const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
424 let child = Command::new("cargo")
425 .args(args)
426 .creation_flags(CREATE_NEW_PROCESS_GROUP)
427 .spawn()?;
428 Ok(child)
429 }
430 #[cfg(not(windows))]
431 {
432 let child = Command::new("cargo").args(args).spawn()?;
433 Ok(child)
434 }
435}