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}