1use crate::prelude::*;
2use crate::Example;
5use once_cell::sync::Lazy;
6
7static GLOBAL_CHILD: Lazy<Arc<Mutex<Option<Child>>>> = Lazy::new(|| Arc::new(Mutex::new(None)));
9
10pub 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
26#[cfg(feature = "equivalent")]
28pub fn run_example(
29 example: &Example,
30 extra_args: &[String],
31) -> Result<std::process::ExitStatus, Box<dyn Error>> {
32 let mut cmd = Command::new("cargo");
34 cmd.args(["run", "--example", &example.name]);
35 if !extra_args.is_empty() {
36 cmd.arg("--").args(extra_args);
37 }
38 use std::process::Stdio;
40 cmd.stdin(Stdio::inherit())
41 .stdout(Stdio::inherit())
42 .stderr(Stdio::inherit());
43
44 let status = cmd.status()?;
45 std::process::exit(status.code().unwrap_or(1));
46}
47
48#[cfg(not(feature = "equivalent"))]
50pub fn run_example(
54 target: &Example,
55 extra_args: &[String],
56) -> Result<std::process::ExitStatus, Box<dyn Error>> {
57 let current_bin = env!("CARGO_PKG_NAME");
59
60 if target.kind == crate::TargetKind::Binary && target.name == current_bin {
62 return Err(format!(
63 "Skipping automatic run: {} is the same as the running binary",
64 target.name
65 )
66 .into());
67 }
68
69 let mut cmd = Command::new("cargo");
70 let manifest_path: PathBuf;
72
73 match target.kind {
74 crate::TargetKind::Example => {
75 if target.extended {
76 println!(
77 "Running extended example in folder: examples/{}",
78 target.name
79 );
80 manifest_path = PathBuf::from(format!("examples/{}/Cargo.toml", target.name));
82 cmd.arg("run")
83 .current_dir(format!("examples/{}", target.name));
84 } else {
85 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
86 cmd.args(["run", "--release", "--example", &target.name]);
87 }
88 }
89 crate::TargetKind::Binary => {
90 println!("Running binary: {}", target.name);
91 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
92 cmd.args(["run", "--release", "--bin", &target.name]);
93 }
94 crate::TargetKind::ExtendedBinary => {
95 println!("Running extended binary: {}", target.name);
96 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
97 cmd.args([
98 "run",
99 "--release",
100 "--manifest-path",
101 &target.manifest_path,
102 "--bin",
103 &target.name,
104 ]);
105 }
106 crate::TargetKind::ExtendedExample => {
107 println!("Running extended example: {}", target.name);
108 manifest_path = PathBuf::from(crate::locate_manifest(false)?);
109 cmd.args([
110 "run",
111 "--release",
112 "--manifest-path",
113 &target.manifest_path,
114 "--example",
115 &target.name,
116 ]);
117 }
118 }
119
120 if !extra_args.is_empty() {
121 cmd.arg("--").args(extra_args);
122 }
123
124 let full_command = format!(
125 "cargo {}",
126 cmd.get_args()
127 .map(|arg| arg.to_string_lossy())
128 .collect::<Vec<_>>()
129 .join(" ")
130 );
131 println!("Running: {}", full_command);
132
133 let maybe_backup = crate::e_manifest::maybe_patch_manifest_for_run(&manifest_path)?;
136
137 let child = cmd.spawn()?;
139 {
140 let mut global = GLOBAL_CHILD.lock().unwrap();
141 *global = Some(child);
142 }
143 let status = {
144 let mut global = GLOBAL_CHILD.lock().unwrap();
145 if let Some(mut child) = global.take() {
146 child.wait()?
147 } else {
148 return Err("Child process missing".into());
149 }
150 };
151
152 if let Some(original) = maybe_backup {
154 fs::write(&manifest_path, original)?;
155 }
156
157 Ok(status)
159}
160pub fn spawn_cargo_process(args: &[&str]) -> Result<Child, Box<dyn Error>> {
163 #[cfg(windows)]
164 {
165 use std::os::windows::process::CommandExt;
166 const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
167 let child = Command::new("cargo")
168 .args(args)
169 .creation_flags(CREATE_NEW_PROCESS_GROUP)
170 .spawn()?;
171 Ok(child)
172 }
173 #[cfg(not(windows))]
174 {
175 let child = Command::new("cargo").args(args).spawn()?;
176 Ok(child)
177 }
178}