1use rand::{distributions::Alphanumeric, thread_rng, Rng};
2use std::{ffi::OsStr, process::Stdio, time::Duration};
3use tokio::{
4 io::{AsyncBufReadExt, BufReader},
5 process::{Child, Command},
6 time::timeout,
7};
8
9pub struct Engine {
10 pub token: String,
11 command: Command,
12 child: Option<Child>,
13}
14
15impl Engine {
16 pub fn new<I, S>(program: &str, args: I) -> Self
17 where
18 I: IntoIterator<Item = S>,
19 S: AsRef<OsStr>,
20 {
21 let secret: String = thread_rng().sample_iter(&Alphanumeric).take(8).collect();
22 let token = format!("token:{}", secret);
23 let mut command = Command::new(program);
24 command
25 .arg("--enable-rpc=true")
26 .arg(format!("--rpc-secret={}", secret))
27 .args(args)
28 .stdin(Stdio::null())
29 .stderr(Stdio::null())
30 .stdout(Stdio::piped());
31 Engine {
32 token,
33 command,
34 child: None,
35 }
36 }
37 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
38 self.command.arg(arg);
39 self
40 }
41 pub async fn start(&mut self) {
42 let mut child = self.command.spawn().unwrap();
43 let output = child.stdout.take().unwrap();
44 let reader = BufReader::new(output);
45 let mut lines = reader.lines();
46 let mut result = false;
47 timeout(Duration::from_secs(10), async {
48 while let Some(line) = lines.next_line().await.unwrap() {
49 if line.contains("[ERROR]") {
50 panic!();
51 };
52 if line.contains("IPv4 RPC: listening on TCP port 6800") {
53 result = true;
54 break;
55 }
56 }
57 })
58 .await
59 .unwrap();
60 if result == false {
61 panic!();
62 }
63 self.child = Some(child);
64 }
65 pub fn stop(&mut self) {
66 if let Some(child) = &mut self.child {
67 child.kill().unwrap();
68 }
69 }
70}