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}