1use cfg_if::cfg_if;
8use std::sync::OnceLock;
9
10cfg_if! {
11 if #[cfg(target_arch = "wasm32")]{
12 use js_sys::Reflect;
13 use wasm_bindgen::prelude::JsValue;
14
15 #[derive(Clone, Copy)]
16 struct JavaScriptRuntime {
17 nodejs : bool,
19 nwjs : bool,
21 electron : bool,
23 browser : bool,
25 web : bool,
27 }
28
29 #[inline(always)]
30 fn exists(property: &str) -> bool {
31 js_sys::Reflect::get(&js_sys::global(), &property.into()).map(|v|!v.is_falsy()).unwrap_or(false)
32 }
33
34 fn exists_prop(object : &JsValue, property: &str) -> bool {
35 js_sys::Reflect::get(object, &property.into()).map(|v|!v.is_falsy()).unwrap_or(false)
36 }
37
38 #[inline]
39 fn detect() -> &'static JavaScriptRuntime {
40 static JAVASCRIPT_RUNTIME: OnceLock<JavaScriptRuntime> = OnceLock::new();
41 JAVASCRIPT_RUNTIME.get_or_init(|| {
42 let global = js_sys::global();
43
44 let mut browser = exists("window") && exists("document") && exists("location") && exists("navigator");
45
46 let process = Reflect::get(&global, &"process".into());
47 let versions = process
48 .clone()
49 .and_then(|process|Reflect::get(&process, &"versions".into()));
50
51 let nodejs = versions
52 .clone()
53 .map(|versions|exists_prop(&versions, "node")).unwrap_or(false);
54
55 let electron = versions
56 .clone()
57 .map(|versions|exists_prop(&versions, "electron")).unwrap_or(false);
58
59
60 if electron {
61 if let Ok(process_type) = process.and_then(|process|Reflect::get(&process, &"type".into())) {
62 browser = process_type.as_string().map(|v|v.as_str() == "renderer").unwrap_or(false);
63 }
64 }
65
66 let nwjs = Reflect::get(&global, &"nw".into())
67 .map(|nw|exists_prop(&nw, "Window")).unwrap_or(false);
68
69 let web = !nodejs && !nwjs && !electron;
70
71 JavaScriptRuntime {
72 nodejs,
73 nwjs,
74 electron,
75 browser,
76 web,
77 }
78 })
79 }
80
81 pub fn is_node() -> bool {
84 detect().nodejs
85 }
86
87 pub fn is_nw() -> bool {
90 detect().nwjs
91 }
92
93 pub fn is_electron() -> bool {
96 detect().electron
97 }
98
99 pub fn is_electron_server() -> bool {
102 detect().electron && !detect().browser
103 }
104
105 pub fn is_electron_client() -> bool {
108 detect().electron && detect().browser
109 }
110
111 pub fn is_web_capable() -> bool {
113 detect().browser
114 }
115
116 pub fn is_web()->bool{
119 detect().web
120 }
121
122 #[inline(always)]
125 pub fn is_cross_origin_isolated()->bool{
126 static CROSS_ORIGIN: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
127 *CROSS_ORIGIN.get_or_init(|| {
128 js_sys::Reflect::get(&js_sys::global(), &"crossOriginIsolated".into())
129 .map(|v| !v.is_falsy())
130 .unwrap_or(false)
131 })
132 }
133 }else{
134
135 pub fn is_node() -> bool {
138 false
139 }
140
141 pub fn is_nw() -> bool {
144 false
145 }
146
147 pub fn is_electron() -> bool {
150 false
151 }
152
153 pub fn is_electron_server() -> bool {
156 false
157 }
158
159 pub fn is_electron_client() -> bool {
162 false
163 }
164
165 pub fn is_web_capable() -> bool {
167 false
168 }
169
170 pub fn is_web()->bool {
173 false
174 }
175
176 #[inline(always)]
179 pub fn is_cross_origin_isolated()->bool{
180 false
181 }
182 }
183}
184
185pub fn is_solana() -> bool {
188 cfg_if! {
189 if #[cfg(target_arch = "bpf")]{
190 true
191 }else{
192 false
193 }
194 }
195}
196
197pub fn is_wasm() -> bool {
200 cfg_if! {
201 if #[cfg(target_arch = "wasm32")]{
202 true
203 }else{
204 false
205 }
206 }
207}
208
209pub fn is_native() -> bool {
212 cfg_if! {
213 if #[cfg(any(target_arch = "bpf", target_arch = "wasm32"))] {
214 false
215 }else{
216 true
217 }
218 }
219}
220
221#[derive(Debug)]
223pub enum Runtime {
224 Native,
225 Solana,
226 NW,
227 Node,
228 Web,
229}
230
231impl From<&Runtime> for String {
232 fn from(value: &Runtime) -> Self {
233 match value {
234 Runtime::Native => "Native",
235 Runtime::Solana => "Solana",
236 Runtime::NW => "NW",
237 Runtime::Node => "Node",
238 Runtime::Web => "Web",
239 }
240 .to_string()
241 }
242}
243
244impl std::fmt::Display for Runtime {
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 let str: String = self.into();
247 f.write_str(&str)
248 }
249}
250
251impl Runtime {
252 pub fn get() -> Self {
254 if is_solana() {
255 Runtime::Solana
256 } else if is_wasm() {
257 if is_nw() {
258 Runtime::NW
259 } else if is_node() {
260 Runtime::Node
261 } else {
262 Runtime::Web
263 }
264 } else {
265 Runtime::Native
266 }
267 }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq)]
271pub enum Platform {
272 Windows,
273 MacOS,
274 Linux,
275 FreeBSD,
276 OpenBSD,
277 NetBSD,
278 Android,
279 IOS,
280 Unknown,
281 Other(String),
282}
283
284impl Platform {
285 pub fn from_node() -> Self {
286 let process = js_sys::Reflect::get(&js_sys::global(), &"process".into())
287 .expect("Unable to get nodejs process global");
288 let platform = js_sys::Reflect::get(&process, &"platform".into())
289 .expect("Unable to get nodejs process.platform");
290
291 let platform = match platform
292 .as_string()
293 .expect("nodejs process.platform is not a string")
294 .as_str()
295 {
296 "win32" => Platform::Windows,
297 "darwin" => Platform::MacOS,
298 "linux" => Platform::Linux,
299 "openbsd" => Platform::OpenBSD,
300 "freebsd" => Platform::FreeBSD,
301 v => Platform::Other(v.to_string()),
302 };
303
304 platform
305 }
306
307 pub fn from_web() -> Self {
308 let window = if let Some(window) = web_sys::window() {
309 window
310 } else {
311 return Platform::Unknown;
312 };
313
314 let user_agent = if let Ok(user_agent) = window.navigator().user_agent() {
315 user_agent.to_lowercase()
316 } else {
317 return Platform::Unknown;
318 };
319
320 if user_agent.contains("win") {
321 Platform::Windows
322 } else if user_agent.contains("mac") {
323 Platform::MacOS
324 } else if user_agent.contains("linux") {
325 Platform::Linux
326 } else if user_agent.contains("android") {
327 Platform::Android
328 } else if user_agent.contains("ios")
329 || user_agent.contains("iphone")
330 || user_agent.contains("ipad")
331 {
332 Platform::IOS
333 } else if user_agent.contains("freebsd") {
334 Platform::FreeBSD
335 } else if user_agent.contains("openbsd") {
336 Platform::OpenBSD
337 } else if user_agent.contains("netbsd") {
338 Platform::NetBSD
339 } else {
340 Platform::Unknown
341 }
342 }
343}
344
345static PLATFORM: OnceLock<Platform> = OnceLock::new();
346
347pub fn platform() -> Platform {
348 PLATFORM
349 .get_or_init(|| {
350 cfg_if! {
351 if #[cfg(target_os = "windows")] {
352 Platform::Windows
353 } else if #[cfg(target_os = "macos")] {
354 Platform::MacOS
355 } else if #[cfg(target_os = "linux")] {
356 Platform::Linux
357 } else if #[cfg(target_os = "android")] {
358 Platform::Android
359 } else if #[cfg(target_os = "ios")] {
360 Platform::IOS
361 } else if #[cfg(target_arch = "wasm32")] {
362 if is_node() {
363 Platform::from_node()
364 } else {
365 Platform::from_web()
366 }
367 }
368 }
369 })
370 .clone()
371}
372
373pub fn is_windows() -> bool {
374 cfg_if! {
375 if #[cfg(target_os = "windows")] {
376 true
377 } else {
378 platform() == Platform::Windows
379 }
380 }
381}
382
383pub fn is_macos() -> bool {
384 cfg_if! {
385 if #[cfg(target_os = "macos")] {
386 true
387 } else {
388 platform() == Platform::MacOS
389 }
390 }
391}
392
393pub fn is_linux() -> bool {
394 cfg_if! {
395 if #[cfg(target_os = "linux")] {
396 true
397 } else {
398 platform() == Platform::Linux
399 }
400 }
401}
402
403pub fn is_freebsd() -> bool {
404 cfg_if! {
405 if #[cfg(target_os = "freebsd")] {
406 true
407 } else {
408 platform() == Platform::FreeBSD
409 }
410 }
411}
412
413pub fn is_openbsd() -> bool {
414 cfg_if! {
415 if #[cfg(target_os = "openbsd")] {
416 true
417 } else {
418 platform() == Platform::OpenBSD
419 }
420 }
421}
422
423pub fn is_netbsd() -> bool {
424 cfg_if! {
425 if #[cfg(target_os = "netbsd")] {
426 true
427 } else {
428 platform() == Platform::NetBSD
429 }
430 }
431}
432
433pub fn is_ios() -> bool {
434 platform() == Platform::IOS
435}
436
437pub fn is_android() -> bool {
438 platform() == Platform::Android
439}
440
441pub fn is_unix() -> bool {
442 is_macos() || is_linux() || is_freebsd() || is_openbsd() || is_netbsd()
443}
444
445pub fn is_mobile() -> bool {
446 is_ios() || is_android()
447}
448
449pub fn is_chrome_extension() -> bool {
450 cfg_if! {
451 if #[cfg(target_arch = "wasm32")] {
452
453 static IS_CHROME_EXTENSION : OnceLock<bool> = OnceLock::new();
454
455 *IS_CHROME_EXTENSION.get_or_init(||{
456 if is_web() {
457 js_sys::Reflect::get(&js_sys::global(), &"location".into())
458 .and_then(|location| { js_sys::Reflect::get(&location, &"protocol".into()) })
459 .map(|protocol|protocol == "chrome-extension:")
460 .unwrap_or(false)
461 } else {
462 false
463 }
464 })
465
466 } else {
467 false
468 }
469 }
470}