use std::{ fmt, io::{BufRead, BufReader}, process::{Child, Command, Stdio}, }; use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System, SystemExt}; #[derive(Debug, Clone)] struct AddrInUse; impl fmt::Display for AddrInUse { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "address is in use") } } pub struct Driver { child: Child, } impl Driver { pub fn new() -> Result { let mut child = Command::new("chromedriver") .stdout(Stdio::piped()) .arg("--port=6444") .spawn()?; let child_id = child.id(); let mut child_out = BufReader::new(child.stdout.as_mut().unwrap()); let mut line = String::new(); let mut sys_info = System::new_with_specifics( RefreshKind::new().with_processes(ProcessRefreshKind::new()), ); // Wait for it to say Chromedriver started successfully loop { child_out.read_line(&mut line).unwrap(); if line.contains("ChromeDriver was started successfully.") { break; } if line.contains("bind() failed: Address already in use") || line.contains("Exiting...") { return Err(anyhow::anyhow!(line)); } // Check if chromedriver has exited sys_info.refresh_all(); if let None = sys_info.process(Pid::from(child_id.clone() as i32)) { let status_code = child.wait()?; println!("{}", status_code.code().unwrap()); return Err(anyhow::anyhow!( "chromedriver exited with status code {}", status_code.code().unwrap_or(1337), )); } } println!("chromedriver started"); Ok(Self { child }) } pub fn exit(&mut self) -> Result<(), anyhow::Error> { self.child.kill()?; Ok(()) } }