rustenium_core/
process.rs

1use std::process::Stdio;
2
3use regex::Regex;
4use tokio::io::{AsyncBufReadExt, BufReader};
5use tokio::process::{Child, Command};
6use tokio::time::{timeout, Duration};
7
8pub struct Process {
9    child: Option<Child>,
10}
11
12impl Process {
13    pub fn create<S, I>(exe_path: S, args: I) -> Process
14    where
15        S: AsRef<str>,
16        I: IntoIterator<Item = String>,
17    {
18        let child = Command::new(exe_path.as_ref())
19            .args(args.into_iter().map(|s| s))
20            .stdout(Stdio::piped())
21            .stderr(Stdio::piped())
22            .spawn()
23            .expect("Failed to start process");
24
25        let child = Some(child);
26
27        return Self { child };
28    }
29
30    #[deprecated]
31    pub async fn wait_for_pattern(&mut self, pattern: &str, timeout_secs: Option<u64>) -> String {
32        let timeout_secs = timeout_secs.unwrap_or(20);
33        let regex = Regex::new(pattern).expect("Invalid regex pattern");
34        let child = self.child.as_mut().unwrap();
35
36        let stdout = child.stdout.as_mut().expect("Failed to access stdout");
37        let stderr = child.stderr.as_mut().expect("Failed to access stderr");
38
39        let mut stdout_lines = BufReader::new(stdout).lines();
40        let mut stderr_lines = BufReader::new(stderr).lines();
41
42        let check_line = |_label: &str, line: Result<Option<String>, _>| -> Option<String> {
43            if let Ok(Some(line)) = line {
44                if let Some(captures) = regex.captures(&line) {
45                    if let Some(url) = captures.get(1) {
46                        return Some(url.as_str().into());
47                    }
48                }
49            }
50            None
51        };
52
53        let timeout_duration = Duration::from_secs(timeout_secs);
54
55        let timeout_result = timeout(timeout_duration, async {
56            loop {
57                tokio::select! {
58                    stdout_line = stdout_lines.next_line() => {
59                        if let Some(line) = check_line("stdout", stdout_line) {
60                            return Some(line);
61                        }
62                    },
63                    stderr_line = stderr_lines.next_line() => {
64                        if let Some(line) = check_line("stderr", stderr_line) {
65                            return  Some(line);
66                        }
67                    }
68                }
69            }
70        })
71        .await;
72
73        match timeout_result {
74            Ok(Some(matched)) => matched,
75            Ok(None) => panic!("Found a pattern but None"),
76            Err(_) => panic!("Timeout reached without finding pattern"),
77        }
78    }
79}
80
81impl Drop for Process {
82    fn drop(&mut self) {
83        if let Some(mut child) = self.child.take() {
84            let _ = child.kill(); // Ensure cleanup
85        }
86    }
87}