1use crate::prelude::*;
2use crate::Example;
5use once_cell::sync::Lazy;
6use std::process::ExitStatus;
7
8static GLOBAL_CHILD: Lazy<Arc<Mutex<Option<Child>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
10
11pub fn register_ctrlc_handler() -> Result<(), Box<dyn Error>> {
14 ctrlc::set_handler(move || {
15 let mut child_lock = GLOBAL_CHILD.lock().unwrap();
16 if let Some(child) = child_lock.as_mut() {
17 eprintln!("Ctrl+C pressed, terminating running child process...");
18 let _ = child.kill();
19 } else {
20 eprintln!("Ctrl+C pressed, no child process running. Exiting nicely.");
21 exit(0);
22 }
23 })?;
24 Ok(())
25}
26
27#[cfg(feature = "equivalent")]
29pub fn run_example(
30 example: &Example,
31 extra_args: &[String],
32) -> Result<std::process::ExitStatus, Box<dyn Error>> {
33 let mut cmd = Command::new("cargo");
35 cmd.args(["run", "--example", &example.name]);
36 if !extra_args.is_empty() {
37 cmd.arg("--").args(extra_args);
38 }
39 use std::process::Stdio;
41 cmd.stdin(Stdio::inherit())
42 .stdout(Stdio::inherit())
43 .stderr(Stdio::inherit());
44
45 let status = cmd.status()?;
46 std::process::exit(status.code().unwrap_or(1));
47}
48
49#[cfg(not(feature = "equivalent"))]
51pub fn run_example(target: &Example, extra_args: &[String]) -> Result<ExitStatus, Box<dyn Error>> {
55 let current_bin = env!("CARGO_PKG_NAME");
57
58 if target.kind == crate::TargetKind::Binary && target.name == current_bin {
60 return Err(format!(
61 "Skipping automatic run: {} is the same as the running binary",
62 target.name
63 )
64 .into());
65 }
66
67 let mut cmd = Command::new("cargo");
68 let manifest_path: PathBuf;
70
71 match target.kind {
72 crate::TargetKind::Example => {
73 if target.extended {
74 println!(
75 "Running extended example in folder: examples/{}",
76 target.name
77 );
78 manifest_path = PathBuf::from(format!("examples/{}/Cargo.toml", target.name));
80 cmd.arg("run")
81 .current_dir(format!("examples/{}", target.name));
82 } else {
83 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
84 cmd.args(["run", "--release", "--example", &target.name]);
85 }
86 }
87 crate::TargetKind::Binary => {
88 println!("Running binary: {}", target.name);
89 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
90 cmd.args(["run", "--release", "--bin", &target.name]);
91 }
92 }
93
94 if !extra_args.is_empty() {
95 cmd.arg("--").args(extra_args);
96 }
97
98 let full_command = format!(
99 "cargo {}",
100 cmd.get_args()
101 .map(|arg| arg.to_string_lossy())
102 .collect::<Vec<_>>()
103 .join(" ")
104 );
105 println!("Running: {}", full_command);
106
107 let maybe_backup = crate::e_manifest::maybe_patch_manifest_for_run(&manifest_path)?;
110
111 let child = cmd.spawn()?;
113 {
114 let mut global = GLOBAL_CHILD.lock().unwrap();
115 *global = Some(child);
116 }
117 let status = {
118 let mut global = GLOBAL_CHILD.lock().unwrap();
119 if let Some(mut child) = global.take() {
120 child.wait()?
121 } else {
122 return Err("Child process missing".into());
123 }
124 };
125
126 if let Some(original) = maybe_backup {
128 fs::write(&manifest_path, original)?;
129 }
130
131 println!("Process exited with status: {:?}", status.code());
132 Ok(status)
133}
134pub fn spawn_cargo_process(args: &[&str]) -> Result<Child, Box<dyn Error>> {
137 #[cfg(windows)]
138 {
139 use std::os::windows::process::CommandExt;
140 const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
141 let child = Command::new("cargo")
142 .args(args)
143 .creation_flags(CREATE_NEW_PROCESS_GROUP)
144 .spawn()?;
145 Ok(child)
146 }
147 #[cfg(not(windows))]
148 {
149 let child = Command::new("cargo").args(args).spawn()?;
150 Ok(child)
151 }
152}