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 && 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 let nwjs = Reflect::get(&global, &"nw".into())
66 .map(|nw|exists_prop(&nw, "Window")).unwrap_or(false);
67
68 let web = !nodejs && !nwjs && !electron;
69
70 JavaScriptRuntime {
71 nodejs,
72 nwjs,
73 electron,
74 browser,
75 web,
76 }
77 })
78 }
79
80 pub fn is_node() -> bool {
83 detect().nodejs
84 }
85
86 pub fn is_nw() -> bool {
89 detect().nwjs
90 }
91
92 pub fn is_electron() -> bool {
95 detect().electron
96 }
97
98 pub fn is_electron_server() -> bool {
101 detect().electron && !detect().browser
102 }
103
104 pub fn is_electron_client() -> bool {
107 detect().electron && detect().browser
108 }
109
110 pub fn is_web_capable() -> bool {
112 detect().browser
113 }
114
115 pub fn is_web()->bool{
118 detect().web
119 }
120
121 #[inline(always)]
124 pub fn is_cross_origin_isolated()->bool{
125 static CROSS_ORIGIN: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
126 *CROSS_ORIGIN.get_or_init(|| {
127 js_sys::Reflect::get(&js_sys::global(), &"crossOriginIsolated".into())
128 .map(|v| !v.is_falsy())
129 .unwrap_or(false)
130 })
131 }
132 }else{
133
134 pub fn is_node() -> bool {
137 false
138 }
139
140 pub fn is_nw() -> bool {
143 false
144 }
145
146 pub fn is_electron() -> bool {
149 false
150 }
151
152 pub fn is_electron_server() -> bool {
155 false
156 }
157
158 pub fn is_electron_client() -> bool {
161 false
162 }
163
164 pub fn is_web_capable() -> bool {
166 false
167 }
168
169 pub fn is_web()->bool {
172 false
173 }
174
175 #[inline(always)]
178 pub fn is_cross_origin_isolated()->bool{
179 false
180 }
181 }
182}
183
184pub fn is_solana() -> bool {
187 cfg_if! {
188 if #[cfg(target_arch = "bpf")]{
189 true
190 }else{
191 false
192 }
193 }
194}
195
196pub fn is_wasm() -> bool {
199 cfg_if! {
200 if #[cfg(target_arch = "wasm32")]{
201 true
202 }else{
203 false
204 }
205 }
206}
207
208pub fn is_native() -> bool {
211 cfg_if! {
212 if #[cfg(any(target_arch = "bpf", target_arch = "wasm32"))] {
213 false
214 }else{
215 true
216 }
217 }
218}
219
220#[derive(Debug)]
222pub enum Runtime {
223 Native,
225 Solana,
227 NW,
229 Node,
231 Web,
233}
234
235impl From<&Runtime> for String {
236 fn from(value: &Runtime) -> Self {
237 match value {
238 Runtime::Native => "Native",
239 Runtime::Solana => "Solana",
240 Runtime::NW => "NW",
241 Runtime::Node => "Node",
242 Runtime::Web => "Web",
243 }
244 .to_string()
245 }
246}
247
248impl std::fmt::Display for Runtime {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 let str: String = self.into();
251 f.write_str(&str)
252 }
253}
254
255impl Runtime {
256 pub fn get() -> Self {
258 if is_solana() {
259 Runtime::Solana
260 } else if is_wasm() {
261 if is_nw() {
262 Runtime::NW
263 } else if is_node() {
264 Runtime::Node
265 } else {
266 Runtime::Web
267 }
268 } else {
269 Runtime::Native
270 }
271 }
272}
273
274#[derive(Debug, Clone, PartialEq, Eq)]
276pub enum Platform {
277 Windows,
279 MacOS,
281 Linux,
283 FreeBSD,
285 OpenBSD,
287 NetBSD,
289 Android,
291 IOS,
293 Unknown,
295 Other(String),
297}
298
299impl Platform {
300 pub fn from_node() -> Self {
303 let process = js_sys::Reflect::get(&js_sys::global(), &"process".into())
304 .expect("Unable to get nodejs process global");
305 let platform = js_sys::Reflect::get(&process, &"platform".into())
306 .expect("Unable to get nodejs process.platform");
307
308 match platform
309 .as_string()
310 .expect("nodejs process.platform is not a string")
311 .as_str()
312 {
313 "win32" => Platform::Windows,
314 "darwin" => Platform::MacOS,
315 "linux" => Platform::Linux,
316 "openbsd" => Platform::OpenBSD,
317 "freebsd" => Platform::FreeBSD,
318 v => Platform::Other(v.to_string()),
319 }
320 }
321
322 pub fn from_web() -> Self {
325 let window = match web_sys::window() {
326 Some(window) => window,
327 _ => {
328 return Platform::Unknown;
329 }
330 };
331
332 let user_agent = match window.navigator().user_agent() {
333 Ok(user_agent) => user_agent.to_lowercase(),
334 _ => {
335 return Platform::Unknown;
336 }
337 };
338
339 if user_agent.contains("win") {
340 Platform::Windows
341 } else if user_agent.contains("mac") {
342 Platform::MacOS
343 } else if user_agent.contains("linux") {
344 Platform::Linux
345 } else if user_agent.contains("android") {
346 Platform::Android
347 } else if user_agent.contains("ios")
348 || user_agent.contains("iphone")
349 || user_agent.contains("ipad")
350 {
351 Platform::IOS
352 } else if user_agent.contains("freebsd") {
353 Platform::FreeBSD
354 } else if user_agent.contains("openbsd") {
355 Platform::OpenBSD
356 } else if user_agent.contains("netbsd") {
357 Platform::NetBSD
358 } else {
359 Platform::Unknown
360 }
361 }
362}
363
364static PLATFORM: OnceLock<Platform> = OnceLock::new();
365
366pub fn platform() -> Platform {
369 PLATFORM
370 .get_or_init(|| {
371 cfg_if! {
372 if #[cfg(target_os = "windows")] {
373 Platform::Windows
374 } else if #[cfg(target_os = "macos")] {
375 Platform::MacOS
376 } else if #[cfg(target_os = "linux")] {
377 Platform::Linux
378 } else if #[cfg(target_os = "android")] {
379 Platform::Android
380 } else if #[cfg(target_os = "ios")] {
381 Platform::IOS
382 } else if #[cfg(target_arch = "wasm32")] {
383 if is_node() {
384 Platform::from_node()
385 } else {
386 Platform::from_web()
387 }
388 }
389 }
390 })
391 .clone()
392}
393
394pub fn is_windows() -> bool {
396 cfg_if! {
397 if #[cfg(target_os = "windows")] {
398 true
399 } else {
400 platform() == Platform::Windows
401 }
402 }
403}
404
405pub fn is_macos() -> bool {
407 cfg_if! {
408 if #[cfg(target_os = "macos")] {
409 true
410 } else {
411 platform() == Platform::MacOS
412 }
413 }
414}
415
416pub fn is_linux() -> bool {
418 cfg_if! {
419 if #[cfg(target_os = "linux")] {
420 true
421 } else {
422 platform() == Platform::Linux
423 }
424 }
425}
426
427pub fn is_freebsd() -> bool {
429 cfg_if! {
430 if #[cfg(target_os = "freebsd")] {
431 true
432 } else {
433 platform() == Platform::FreeBSD
434 }
435 }
436}
437
438pub fn is_openbsd() -> bool {
440 cfg_if! {
441 if #[cfg(target_os = "openbsd")] {
442 true
443 } else {
444 platform() == Platform::OpenBSD
445 }
446 }
447}
448
449pub fn is_netbsd() -> bool {
451 cfg_if! {
452 if #[cfg(target_os = "netbsd")] {
453 true
454 } else {
455 platform() == Platform::NetBSD
456 }
457 }
458}
459
460pub fn is_ios() -> bool {
462 platform() == Platform::IOS
463}
464
465pub fn is_android() -> bool {
467 platform() == Platform::Android
468}
469
470pub fn is_unix() -> bool {
472 is_macos() || is_linux() || is_freebsd() || is_openbsd() || is_netbsd()
473}
474
475pub fn is_mobile() -> bool {
477 is_ios() || is_android()
478}
479
480pub fn is_chrome_extension() -> bool {
482 cfg_if! {
483 if #[cfg(target_arch = "wasm32")] {
484
485 static IS_CHROME_EXTENSION : OnceLock<bool> = OnceLock::new();
486
487 *IS_CHROME_EXTENSION.get_or_init(||{
488 if is_web() {
489 js_sys::Reflect::get(&js_sys::global(), &"location".into())
490 .and_then(|location| { js_sys::Reflect::get(&location, &"protocol".into()) })
491 .map(|protocol|protocol == "chrome-extension:")
492 .unwrap_or(false)
493 } else {
494 false
495 }
496 })
497
498 } else {
499 false
500 }
501 }
502}