Skip to main content

chrome_for_testing/api/
platform.rs

1use crate::error::Error;
2use rootcause::{Report, report};
3use serde::{Deserialize, Serialize};
4use std::borrow::Cow;
5use std::env::consts;
6use std::fmt::{Display, Formatter};
7use std::path::Path;
8use std::str::FromStr;
9
10/// Error returned when parsing a platform string fails.
11#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
12#[error("Unknown platform: '{value}'. Expected one of: linux64, mac-arm64, mac-x64, win32, win64")]
13pub struct ParsePlatformError {
14    value: String,
15}
16
17/// Supported platforms for Chrome and `ChromeDriver` downloads.
18///
19/// This site <https://googlechromelabs.github.io/chrome-for-testing/> show the platform names
20/// defined here.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
22pub enum Platform {
23    /// Linux x64 platform.
24    #[serde(rename = "linux64")]
25    Linux64,
26
27    /// macOS ARM64 platform (Apple Silicon).
28    #[serde(rename = "mac-arm64")]
29    MacArm64,
30
31    /// macOS x64 platform (Intel).
32    #[serde(rename = "mac-x64")]
33    MacX64,
34
35    /// Windows 32-bit platform.
36    #[serde(rename = "win32")]
37    Win32,
38
39    /// Windows 64-bit platform.
40    #[serde(rename = "win64")]
41    Win64,
42}
43
44impl Display for Platform {
45    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46        f.write_str(match self {
47            Platform::Linux64 => "linux64",
48            Platform::MacArm64 => "mac-arm64",
49            Platform::MacX64 => "mac-x64",
50            Platform::Win32 => "win32",
51            Platform::Win64 => "win64",
52        })
53    }
54}
55
56impl FromStr for Platform {
57    type Err = Report<ParsePlatformError>;
58
59    fn from_str(s: &str) -> Result<Self, Self::Err> {
60        match s {
61            "linux64" => Ok(Platform::Linux64),
62            "mac-arm64" => Ok(Platform::MacArm64),
63            "mac-x64" => Ok(Platform::MacX64),
64            "win32" => Ok(Platform::Win32),
65            "win64" => Ok(Platform::Win64),
66            _ => Err(report!(ParsePlatformError {
67                value: s.to_owned(),
68            })),
69        }
70    }
71}
72
73impl Platform {
74    /// Detect the platform identifier that should be used for the current system.
75    ///
76    /// # Errors
77    ///
78    /// Returns an error if the current OS/architecture combination is not supported.
79    pub fn detect() -> crate::Result<Platform> {
80        match consts::OS {
81            os @ "windows" => match consts::ARCH {
82                "x86" => Ok(Platform::Win32),
83                "x86_64" => Ok(Platform::Win64),
84                arch => Err(report!(Error::UnsupportedPlatform {
85                    os: Cow::Borrowed(os),
86                    arch: Cow::Borrowed(arch),
87                })),
88            },
89            os @ "linux" => match consts::ARCH {
90                "x86_64" => Ok(Platform::Linux64),
91                arch => Err(report!(Error::UnsupportedPlatform {
92                    os: Cow::Borrowed(os),
93                    arch: Cow::Borrowed(arch),
94                })),
95            },
96            os @ "macos" => match consts::ARCH {
97                "x86_64" => Ok(Platform::MacX64),
98                "arm" | "aarch64" => Ok(Platform::MacArm64),
99                arch => Err(report!(Error::UnsupportedPlatform {
100                    os: Cow::Borrowed(os),
101                    arch: Cow::Borrowed(arch),
102                })),
103            },
104            os => Err(report!(Error::UnsupportedPlatform {
105                os: Cow::Borrowed(os),
106                arch: Cow::Borrowed(consts::ARCH),
107            })),
108        }
109    }
110
111    /// Filename of the Chrome executable.
112    #[must_use]
113    pub fn chrome_executable_name(self) -> &'static str {
114        match self {
115            Platform::Linux64 => "chrome",
116            Platform::MacArm64 | Platform::MacX64 => "Google Chrome for Testing",
117            Platform::Win32 | Platform::Win64 => "chrome.exe",
118        }
119    }
120
121    /// Relative path of the Chrome executable inside the unpacked Chrome archive.
122    #[must_use]
123    pub fn chrome_executable_path(self) -> &'static Path {
124        match self {
125            Platform::Linux64 => Path::new("chrome-linux64/chrome"),
126            Platform::MacArm64 => Path::new(
127                "chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing",
128            ),
129            Platform::MacX64 => Path::new(
130                "chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing",
131            ),
132            Platform::Win32 => Path::new("chrome-win32/chrome.exe"),
133            Platform::Win64 => Path::new("chrome-win64/chrome.exe"),
134        }
135    }
136
137    /// Filename of the `ChromeDriver` executable.
138    #[must_use]
139    pub fn chromedriver_executable_name(self) -> &'static str {
140        match self {
141            Platform::Linux64 | Platform::MacX64 | Platform::MacArm64 => "chromedriver",
142            Platform::Win32 | Platform::Win64 => "chromedriver.exe",
143        }
144    }
145
146    /// Relative path of the `ChromeDriver` executable inside the unpacked `ChromeDriver` archive.
147    #[must_use]
148    pub fn chromedriver_executable_path(self) -> &'static Path {
149        match self {
150            Platform::Linux64 => Path::new("chromedriver-linux64/chromedriver"),
151            Platform::MacArm64 => Path::new("chromedriver-mac-arm64/chromedriver"),
152            Platform::MacX64 => Path::new("chromedriver-mac-x64/chromedriver"),
153            Platform::Win32 => Path::new("chromedriver-win32/chromedriver.exe"),
154            Platform::Win64 => Path::new("chromedriver-win64/chromedriver.exe"),
155        }
156    }
157
158    /// Filename of the Chrome Headless Shell executable.
159    #[must_use]
160    pub fn chrome_headless_shell_executable_name(self) -> &'static str {
161        match self {
162            Platform::Linux64 | Platform::MacX64 | Platform::MacArm64 => "chrome-headless-shell",
163            Platform::Win32 | Platform::Win64 => "chrome-headless-shell.exe",
164        }
165    }
166
167    /// Relative path of the Chrome Headless Shell executable inside the unpacked
168    /// Chrome Headless Shell archive.
169    #[must_use]
170    pub fn chrome_headless_shell_executable_path(self) -> &'static Path {
171        match self {
172            Platform::Linux64 => Path::new("chrome-headless-shell-linux64/chrome-headless-shell"),
173            Platform::MacArm64 => {
174                Path::new("chrome-headless-shell-mac-arm64/chrome-headless-shell")
175            }
176            Platform::MacX64 => Path::new("chrome-headless-shell-mac-x64/chrome-headless-shell"),
177            Platform::Win32 => Path::new("chrome-headless-shell-win32/chrome-headless-shell.exe"),
178            Platform::Win64 => Path::new("chrome-headless-shell-win64/chrome-headless-shell.exe"),
179        }
180    }
181
182    /// Tells whether this platform identifier references the Linux OS.
183    #[must_use]
184    pub fn is_linux(&self) -> bool {
185        match self {
186            Platform::Linux64 => true,
187            Platform::MacArm64 | Platform::MacX64 | Platform::Win32 | Platform::Win64 => false,
188        }
189    }
190
191    /// Tells whether this platform identifier references macOS.
192    #[must_use]
193    pub fn is_macos(&self) -> bool {
194        match self {
195            Platform::MacArm64 | Platform::MacX64 => true,
196            Platform::Linux64 | Platform::Win32 | Platform::Win64 => false,
197        }
198    }
199
200    /// Tells whether this platform identifier references the Windows OS.
201    #[must_use]
202    pub fn is_windows(&self) -> bool {
203        match self {
204            Platform::Win32 | Platform::Win64 => true,
205            Platform::Linux64 | Platform::MacArm64 | Platform::MacX64 => false,
206        }
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use assertr::prelude::*;
214
215    #[test]
216    fn parse_to_string_round_trip() {
217        let platforms = [
218            ("linux64", Platform::Linux64),
219            ("mac-arm64", Platform::MacArm64),
220            ("mac-x64", Platform::MacX64),
221            ("win32", Platform::Win32),
222            ("win64", Platform::Win64),
223        ];
224        for (s, expected) in platforms {
225            assert_that!(s.parse::<Platform>())
226                .is_ok()
227                .is_equal_to(expected);
228            assert_that!(expected.to_string()).is_equal_to(s);
229        }
230    }
231
232    #[test]
233    fn parse_invalid_variant_fails() {
234        assert_that!("Linux64".parse::<Platform>()).is_err();
235        assert_that!("unknown".parse::<Platform>()).is_err();
236    }
237
238    #[test]
239    fn executable_path_file_names_match_executable_names() {
240        let platforms = [
241            Platform::Linux64,
242            Platform::MacArm64,
243            Platform::MacX64,
244            Platform::Win32,
245            Platform::Win64,
246        ];
247
248        for platform in platforms {
249            assert_that!(
250                platform
251                    .chrome_executable_path()
252                    .file_name()
253                    .and_then(|it| it.to_str())
254            )
255            .is_equal_to(Some(platform.chrome_executable_name()));
256            assert_that!(
257                platform
258                    .chromedriver_executable_path()
259                    .file_name()
260                    .and_then(|it| it.to_str())
261            )
262            .is_equal_to(Some(platform.chromedriver_executable_name()));
263            assert_that!(
264                platform
265                    .chrome_headless_shell_executable_path()
266                    .file_name()
267                    .and_then(|it| it.to_str())
268            )
269            .is_equal_to(Some(platform.chrome_headless_shell_executable_name()));
270        }
271    }
272
273    #[test]
274    fn serialized_value_matches_display_output() {
275        assert_that!(serde_json::to_string(&Platform::Linux64).unwrap())
276            .is_equal_to(String::from("\"linux64\""));
277        assert_that!(serde_json::to_string(&Platform::MacArm64).unwrap())
278            .is_equal_to(String::from("\"mac-arm64\""));
279    }
280}