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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
22pub enum Platform {
23 #[serde(rename = "linux64")]
25 Linux64,
26
27 #[serde(rename = "mac-arm64")]
29 MacArm64,
30
31 #[serde(rename = "mac-x64")]
33 MacX64,
34
35 #[serde(rename = "win32")]
37 Win32,
38
39 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}