chromedriver_manager/
manager.rs1use crate::utils::{
7 appdata::get_cache_dir,
8 downloader::{download_chrome, download_chromedriver},
9 functions::{get_latest_chrome_package, get_platform},
10};
11
12use crate::{
13 loglevel::LogLevel,
14 structs::{
15 chrome::{ChromeDownload, DriverDownload},
16 packages::ChromePackage,
17 },
18};
19
20use std::{
21 path::PathBuf,
22 process::{self, Command},
23};
24
25use anyhow::{Context, Ok};
26use thirtyfour::{ChromeCapabilities, ChromiumLikeCapabilities};
27
28const CHROME_DOWNLOADS_URL: &str =
29 "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json";
30
31pub struct Handler {
32 client: reqwest::Client,
33 platform: String,
34}
35
36impl Default for Handler {
37 fn default() -> Self {
38 Self {
39 client: reqwest::Client::new(),
40 platform: get_platform(),
41 }
42 }
43}
44
45impl Handler {
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 fn get_default_paths(&self) -> (PathBuf, PathBuf) {
51 let chrome_path = get_cache_dir().join(format!("chrome-{}", self.platform));
52 let driver_path = get_cache_dir().join(format!("chromedriver-{}", self.platform));
53
54 (chrome_path, driver_path)
55 }
56
57 fn package_downloaded(&self) -> bool {
58 let (chrome_path, driver_path) = self.get_default_paths();
59
60 if chrome_path.exists() && driver_path.exists() {
61 return true;
62 }
63
64 false
65 }
66
67 async fn get_packages(&self) -> anyhow::Result<Vec<ChromePackage>> {
68 let client = reqwest::Client::new();
69
70 let response: serde_json::Value = client
71 .get(CHROME_DOWNLOADS_URL)
72 .send()
73 .await?
74 .text()
75 .await?
76 .parse()?;
77
78 let chrome_packages: serde_json::Value = response["versions"].clone();
79 let chrome_packages: Vec<ChromePackage> = serde_json::from_value(chrome_packages)?;
80
81 Ok(chrome_packages)
82 }
83
84 async fn get_selected_package(
86 &self,
87 packages: &Vec<ChromePackage>,
88 ) -> anyhow::Result<ChromePackage> {
89 get_latest_chrome_package(packages).context("Could not get the latest version of chrome")
90 }
91
92 async fn download_files(&self) -> anyhow::Result<(PathBuf, PathBuf)> {
93 let chrome_packages = self.get_packages().await?;
94 let selected_package = self.get_selected_package(&chrome_packages).await?;
95
96 let chrome_download: &ChromeDownload = selected_package
98 .get_chrome_download(&self.platform)
99 .expect("Chrome download not found");
100
101 let chromedriver_download: &DriverDownload = selected_package
102 .get_chromedriver_download(&self.platform)
103 .expect("Chromedriver download not found");
104
105 let version = &selected_package.version;
108 println!(
109 "Installing chrome version {}.{}.{}.{}...\n",
110 version.major, version.minor, version.patch, version.build
111 );
112
113 download_chrome(&self.client, chrome_download).await?;
114 println!();
115 download_chromedriver(&self.client, chromedriver_download).await?;
116
117 let chrome_path = chrome_download.to_folder_path();
118 let driver_path = chromedriver_download.to_folder_path();
119
120 println!("Chrome path: {:?}", chrome_path);
121 println!("Chromedriver path: {:?}", driver_path);
122
123 Ok((chrome_path, driver_path))
124 }
125
126 fn get_file_names(&self) -> (String, String) {
128 let chrome_exe: String;
129 let chromedriver_exe: String;
130
131 if cfg!(target_os = "windows") {
132 chrome_exe = "chrome.exe".to_string();
133 chromedriver_exe = "chromedriver.exe".to_string();
134 } else if cfg!(target_os = "macos") {
135 chrome_exe = "Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing".to_string();
136 chromedriver_exe = "chromedriver".to_string();
137 } else {
138 chrome_exe = "chrome".to_string();
139 chromedriver_exe = "chromedriver".to_string();
140 }
141
142 (chrome_exe, chromedriver_exe)
143 }
144
145 pub async fn launch_chromedriver(
146 &mut self,
147 capabilities: &mut ChromeCapabilities,
148 port: &str,
149 loglevel: LogLevel,
150 ) -> Result<process::Child, anyhow::Error> {
151 self.client = reqwest::Client::new();
152
153 let chrome_exe: PathBuf;
154 let chromedriver_exe: PathBuf;
155
156 let (chrome_exe_name, chromedriver_exe_name) = self.get_file_names();
157
158 if !self.package_downloaded() {
159 let (chrome_path, driver_path) = self.download_files().await?;
160
161 chrome_exe = chrome_path.join(chrome_exe_name);
162 chromedriver_exe = driver_path.join(chromedriver_exe_name);
163 } else {
164 let (default_chrome_path, default_driver_path) = self.get_default_paths();
165
166 chrome_exe = default_chrome_path.join(chrome_exe_name);
167 chromedriver_exe = default_driver_path.join(chromedriver_exe_name);
168 }
169
170 capabilities.set_binary(chrome_exe.to_str().unwrap())?;
171
172 let chromedriver_exe = chromedriver_exe.to_str().unwrap();
173
174 let mut command = Command::new(chromedriver_exe);
175 let mut command = command
176 .arg(format!("--port={}", port))
177 .arg(format!("--log-level={loglevel}"));
178
179 if loglevel == LogLevel::Off {
180 command = self.apply_creation_flags(command);
182 }
183
184 Ok(command.spawn()?)
185 }
186
187 #[cfg(target_os = "windows")]
188 fn apply_creation_flags<'a>(&self, command: &'a mut Command) -> &'a mut Command {
189 use std::os::windows::process::CommandExt;
190
191 command.creation_flags(0x08000000)
192 }
193
194 #[cfg(not(target_os = "windows"))]
195 fn apply_creation_flags<'a>(&self, command: &'a mut Command) -> &'a mut Command {
196 command
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use std::{thread, time::Duration};
203
204 use crate::{loglevel::LogLevel, manager::Handler};
205 use thirtyfour::prelude::*;
206
207 #[tokio::test]
208 async fn test_launch_chromedriver() -> anyhow::Result<()> {
209 let mut caps = DesiredCapabilities::chrome();
210
211 caps.set_headless()?;
212 caps.set_no_sandbox()?;
213 caps.set_disable_gpu()?;
214
215 let mut chromedriver = Handler::new()
217 .launch_chromedriver(&mut caps, "3000", LogLevel::Off)
218 .await?;
219
220 println!("Launched Chromedriver");
221
222 let driver = WebDriver::new("http://localhost:3000", caps).await?;
223 driver.goto("https://www.gimkit.com/join").await?;
224
225 thread::sleep(Duration::from_secs(10));
226
227 driver.quit().await?;
228 chromedriver.kill()?;
229
230 Ok(())
231 }
232}