Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/rust/src/config.rs
2885 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License. You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied. See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
use crate::config::OS::{LINUX, MACOS, WINDOWS};
19
use crate::shell::run_shell_command_by_os;
20
use crate::{
21
default_cache_folder, format_one_arg, path_to_string, Command, ARCH_ARM7L,
22
ENV_PROCESSOR_ARCHITECTURE, REQUEST_TIMEOUT_SEC, UNAME_COMMAND,
23
};
24
use crate::{ARCH_ARM64, ARCH_X64, ARCH_X86, TTL_SEC};
25
use anyhow::anyhow;
26
use anyhow::Error;
27
use std::cell::RefCell;
28
use std::env;
29
use std::env::consts::OS;
30
use std::fs::read_to_string;
31
use std::path::Path;
32
use toml::Table;
33
#[cfg(windows)]
34
use winapi::um::sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO};
35
#[cfg(windows)]
36
use winapi::um::winnt::{
37
PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_ARM, PROCESSOR_ARCHITECTURE_ARM64,
38
PROCESSOR_ARCHITECTURE_IA64, PROCESSOR_ARCHITECTURE_INTEL,
39
};
40
41
thread_local!(static CACHE_PATH: RefCell<String> = RefCell::new(path_to_string(&default_cache_folder())));
42
43
pub const CONFIG_FILE: &str = "se-config.toml";
44
pub const ENV_PREFIX: &str = "SE_";
45
pub const VERSION_PREFIX: &str = "-version";
46
pub const PATH_PREFIX: &str = "-path";
47
pub const MIRROR_PREFIX: &str = "-mirror-url";
48
pub const CACHE_PATH_KEY: &str = "cache-path";
49
50
pub struct ManagerConfig {
51
pub cache_path: String,
52
pub fallback_driver_from_cache: bool,
53
pub browser_version: String,
54
pub driver_version: String,
55
pub browser_path: String,
56
pub driver_mirror_url: String,
57
pub browser_mirror_url: String,
58
pub os: String,
59
pub arch: String,
60
pub proxy: String,
61
pub timeout: u64,
62
pub ttl: u64,
63
pub offline: bool,
64
pub force_browser_download: bool,
65
pub avoid_browser_download: bool,
66
pub language_binding: String,
67
pub selenium_version: String,
68
pub avoid_stats: bool,
69
pub skip_driver_in_path: bool,
70
pub skip_browser_in_path: bool,
71
}
72
73
impl ManagerConfig {
74
pub fn default(browser_name: &str, driver_name: &str) -> ManagerConfig {
75
let cache_path = StringKey(vec![CACHE_PATH_KEY], &read_cache_path()).get_value();
76
77
let self_os = OS;
78
let self_arch = if WINDOWS.is(self_os) {
79
let mut _architecture = env::var(ENV_PROCESSOR_ARCHITECTURE).unwrap_or_default();
80
#[cfg(windows)]
81
{
82
if _architecture.is_empty() {
83
_architecture = get_win_os_architecture();
84
}
85
}
86
if _architecture.contains("32") {
87
ARCH_X86.to_string()
88
} else if _architecture.contains("ARM") {
89
ARCH_ARM64.to_string()
90
} else {
91
ARCH_X64.to_string()
92
}
93
} else {
94
let uname_a_command = Command::new_single(format_one_arg(UNAME_COMMAND, "a"));
95
if run_shell_command_by_os(self_os, uname_a_command)
96
.unwrap_or_default()
97
.to_ascii_lowercase()
98
.contains(ARCH_ARM64)
99
{
100
ARCH_ARM64.to_string()
101
} else {
102
let uname_m_command = Command::new_single(format_one_arg(UNAME_COMMAND, "m"));
103
run_shell_command_by_os(self_os, uname_m_command).unwrap_or_default()
104
}
105
};
106
107
let browser_version_label = concat(browser_name, VERSION_PREFIX);
108
let driver_version_label = concat(driver_name, VERSION_PREFIX);
109
let browser_path_label = concat(browser_name, PATH_PREFIX);
110
let driver_mirror_label = concat(driver_name, MIRROR_PREFIX);
111
let browser_mirror_label = concat(browser_name, MIRROR_PREFIX);
112
113
ManagerConfig {
114
cache_path,
115
fallback_driver_from_cache: true,
116
browser_version: StringKey(vec!["browser-version", &browser_version_label], "")
117
.get_value(),
118
driver_version: StringKey(vec!["driver-version", &driver_version_label], "")
119
.get_value(),
120
browser_path: StringKey(vec!["browser-path", &browser_path_label], "").get_value(),
121
driver_mirror_url: StringKey(vec!["driver-mirror-url", &driver_mirror_label], "")
122
.get_value(),
123
browser_mirror_url: StringKey(vec!["browser-mirror-url", &browser_mirror_label], "")
124
.get_value(),
125
os: StringKey(vec!["os"], self_os).get_value(),
126
arch: StringKey(vec!["arch"], self_arch.as_str()).get_value(),
127
proxy: StringKey(vec!["proxy"], "").get_value(),
128
timeout: IntegerKey("timeout", REQUEST_TIMEOUT_SEC).get_value(),
129
ttl: IntegerKey("ttl", TTL_SEC).get_value(),
130
offline: BooleanKey("offline", false).get_value(),
131
force_browser_download: BooleanKey("force-browser-download", false).get_value(),
132
avoid_browser_download: BooleanKey("avoid-browser-download", false).get_value(),
133
language_binding: StringKey(vec!["language-binding"], "").get_value(),
134
selenium_version: StringKey(vec!["selenium-version"], "").get_value(),
135
avoid_stats: BooleanKey("avoid-stats", false).get_value(),
136
skip_driver_in_path: BooleanKey("skip-driver-in-path", false).get_value(),
137
skip_browser_in_path: BooleanKey("skip-browser-in-path", false).get_value(),
138
}
139
}
140
}
141
142
#[allow(dead_code)]
143
#[allow(clippy::upper_case_acronyms)]
144
#[derive(Hash, Eq, PartialEq, Debug)]
145
pub enum OS {
146
WINDOWS,
147
MACOS,
148
LINUX,
149
}
150
151
impl OS {
152
pub fn to_str_vector(&self) -> Vec<&str> {
153
match self {
154
WINDOWS => vec!["windows", "win"],
155
MACOS => vec!["macos", "mac"],
156
LINUX => vec!["linux", "gnu/linux"],
157
}
158
}
159
160
pub fn is(&self, os: &str) -> bool {
161
self.to_str_vector()
162
.contains(&os.to_ascii_lowercase().as_str())
163
}
164
}
165
166
pub fn str_to_os(os: &str) -> Result<OS, Error> {
167
if WINDOWS.is(os) {
168
Ok(WINDOWS)
169
} else if MACOS.is(os) {
170
Ok(MACOS)
171
} else if LINUX.is(os) {
172
Ok(LINUX)
173
} else {
174
Err(anyhow!(format!("Invalid operating system: {os}")))
175
}
176
}
177
178
#[allow(dead_code)]
179
#[allow(clippy::upper_case_acronyms)]
180
pub enum ARCH {
181
X32,
182
X64,
183
ARM64,
184
ARMV7,
185
}
186
187
impl ARCH {
188
pub fn to_str_vector(&self) -> Vec<&str> {
189
match self {
190
ARCH::X32 => vec![ARCH_X86, "i386", "x32"],
191
ARCH::X64 => vec![ARCH_X64, "amd64", "x64", "i686", "ia64"],
192
ARCH::ARM64 => vec![ARCH_ARM64, "aarch64", "arm"],
193
ARCH::ARMV7 => vec![ARCH_ARM7L, "armv7l"],
194
}
195
}
196
197
pub fn is(&self, arch: &str) -> bool {
198
self.to_str_vector()
199
.contains(&arch.to_ascii_lowercase().as_str())
200
}
201
}
202
203
pub struct StringKey<'a>(pub Vec<&'a str>, pub &'a str);
204
205
impl StringKey<'_> {
206
pub fn get_value(&self) -> String {
207
let config = get_config().unwrap_or_default();
208
let keys = self.0.to_owned();
209
let default_value = self.1.to_owned();
210
let mut result;
211
for key in keys {
212
if config.contains_key(key) {
213
result = config[key].as_str().unwrap().to_string()
214
} else {
215
result = env::var(get_env_name(key)).unwrap_or_default()
216
}
217
if key.eq(CACHE_PATH_KEY) {
218
// The configuration key for the cache path ("cache-path") is special because
219
// the rest of the configuration values depend on this value (since the
220
// configuration file is stored in the cache path). Therefore, this value needs
221
// to be discovered in the first place and stored globally (on CACHE_PATH)
222
return check_cache_path(result, default_value);
223
}
224
if !result.is_empty() {
225
return result;
226
}
227
}
228
default_value
229
}
230
}
231
232
pub struct IntegerKey<'a>(pub &'a str, pub u64);
233
234
impl IntegerKey<'_> {
235
pub fn get_value(&self) -> u64 {
236
let config = get_config().unwrap_or_default();
237
let key = self.0;
238
if config.contains_key(key) {
239
config[key].as_integer().unwrap() as u64
240
} else {
241
env::var(get_env_name(key))
242
.unwrap_or_default()
243
.parse::<u64>()
244
.unwrap_or_else(|_| self.1.to_owned())
245
}
246
}
247
}
248
249
pub struct BooleanKey<'a>(pub &'a str, pub bool);
250
251
impl BooleanKey<'_> {
252
pub fn get_value(&self) -> bool {
253
let config = get_config().unwrap_or_default();
254
let key = self.0;
255
if config.contains_key(key) {
256
config[key].as_bool().unwrap()
257
} else {
258
env::var(get_env_name(key))
259
.unwrap_or_default()
260
.parse::<bool>()
261
.unwrap_or_else(|_| self.1.to_owned())
262
}
263
}
264
}
265
266
fn get_env_name(suffix: &str) -> String {
267
let suffix_uppercase: String = suffix.replace('-', "_").to_uppercase();
268
concat(ENV_PREFIX, suffix_uppercase.as_str())
269
}
270
271
fn get_config() -> Result<Table, Error> {
272
let cache_path = read_cache_path();
273
let config_path = Path::new(&cache_path).to_path_buf().join(CONFIG_FILE);
274
Ok(read_to_string(config_path)?.parse()?)
275
}
276
277
fn concat(prefix: &str, suffix: &str) -> String {
278
let mut version_label: String = prefix.to_owned();
279
version_label.push_str(suffix);
280
version_label
281
}
282
283
fn check_cache_path(value_in_config_or_env: String, default_value: String) -> String {
284
let return_value = if !value_in_config_or_env.is_empty() {
285
value_in_config_or_env
286
} else if !default_value.is_empty() {
287
default_value
288
} else {
289
read_cache_path()
290
};
291
write_cache_path(return_value.clone());
292
return_value
293
}
294
295
fn write_cache_path(cache_path: String) {
296
CACHE_PATH.with(|value| {
297
*value.borrow_mut() = cache_path;
298
});
299
}
300
301
fn read_cache_path() -> String {
302
let mut cache_path: String = path_to_string(&default_cache_folder());
303
CACHE_PATH.with(|value| {
304
let path: String = (&*value.borrow().to_string()).into();
305
if !path.is_empty() {
306
cache_path = path;
307
}
308
});
309
cache_path
310
}
311
312
#[cfg(windows)]
313
fn get_win_os_architecture() -> String {
314
unsafe {
315
let mut system_info: SYSTEM_INFO = std::mem::zeroed();
316
GetNativeSystemInfo(&mut system_info);
317
318
match system_info.u.s() {
319
si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 => "64-bit",
320
si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL => "32-bit",
321
si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM => "ARM",
322
si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64 => "ARM64",
323
si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 => "Itanium-based",
324
_ => "Unknown",
325
}
326
.to_string()
327
}
328
}
329
330