1use std::ffi::OsStr;
2use std::io::{Read, Write};
3use std::process::{Child, Stdio};
4
5#[macro_export]
8macro_rules! run_current_example {
9 () => {
10 $crate::run_current_example!(::std::iter::empty::<&str>())
11 };
12 ($args:literal) => {
13 $crate::run_current_example!(str::split_whitespace($args))
14 };
15 ($args:expr $(,)?) => {
16 $crate::ExampleChild::run_new(
17 &::std::env::var("CARGO_PKG_NAME").unwrap(),
18 &$crate::extract_example_name(file!()).expect("Failed to determine example name."),
19 $args,
20 )
21 };
22}
23
24pub struct ExampleChild {
28 child: Child,
29 output_buffer: Vec<u8>,
30 output_len: usize,
31}
32impl ExampleChild {
33 pub fn run_new(
34 pkg_name: &str,
35 test_name: &str,
36 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
37 ) -> Self {
38 let child = std::process::Command::new("cargo")
39 .args(["run", "-p", pkg_name, "--example"])
40 .arg(test_name)
41 .arg("--")
42 .args(args)
43 .stdin(Stdio::piped())
44 .stdout(Stdio::piped())
45 .spawn()
46 .unwrap();
47 Self {
48 child,
49 output_buffer: vec![0; 1024],
50 output_len: 0,
51 }
52 }
53
54 pub fn read_string(&mut self, wait_for_string: &str) {
62 self.read_regex(®ex::escape(wait_for_string));
63 }
64
65 pub fn read_regex(&mut self, wait_for_regex: &str) {
67 let stdout = self.child.stdout.as_mut().unwrap();
68 let re = regex::Regex::new(wait_for_regex).unwrap();
69
70 while !re.is_match(&String::from_utf8_lossy(
71 &self.output_buffer[0..self.output_len],
72 )) {
73 eprintln!(
74 "waiting ({}):\n{}",
75 wait_for_regex,
76 String::from_utf8_lossy(&self.output_buffer[0..self.output_len])
77 );
78
79 while self.output_buffer.len() - self.output_len < 1024 {
80 self.output_buffer
81 .resize(self.output_buffer.len() + 1024, 0);
82 }
83
84 let bytes_read = stdout
85 .read(&mut self.output_buffer[self.output_len..])
86 .unwrap();
87 self.output_len += bytes_read;
88
89 if 0 == bytes_read {
90 panic!("Child process exited before a match was found.");
91 }
92 }
93 }
94
95 pub fn write_line(&mut self, line: &str) {
97 let stdin = self.child.stdin.as_mut().unwrap();
98 stdin.write_all(line.as_bytes()).unwrap();
99 stdin.write_all(b"\n").unwrap();
100 stdin.flush().unwrap();
101 }
102}
103
104impl Drop for ExampleChild {
109 fn drop(&mut self) {
110 #[cfg(target_family = "windows")]
111 let _ = self.child.kill(); #[cfg(not(target_family = "windows"))]
113 self.child.kill().unwrap();
114
115 self.child.wait().unwrap();
116 }
117}
118
119pub fn extract_example_name(file: &str) -> Option<String> {
121 let pathbuf = std::path::PathBuf::from(file);
122 let mut path = pathbuf.as_path();
123 while path.parent()?.file_name()? != "examples" {
124 path = path.parent().unwrap();
125 }
126 Some(path.file_stem()?.to_string_lossy().into_owned())
127}