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