use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "wasm32")]{
use js_sys::Reflect;
use wasm_bindgen::prelude::JsValue;
#[derive(Clone, Copy)]
struct JavaScriptRuntime {
nodejs : bool,
nwjs : bool,
electron : bool,
browser : bool,
web : bool,
}
#[inline(always)]
fn exists(property: &str) -> bool {
js_sys::Reflect::get(&js_sys::global(), &property.into()).map(|v|!v.is_falsy()).unwrap_or(false)
}
fn exists_prop(object : &JsValue, property: &str) -> bool {
js_sys::Reflect::get(object, &property.into()).map(|v|!v.is_falsy()).unwrap_or(false)
}
#[inline]
fn detect() -> &'static JavaScriptRuntime {
static mut JAVASCRIPT_RUNTIME: Option<JavaScriptRuntime> = None;
unsafe {
JAVASCRIPT_RUNTIME.get_or_insert_with(||{
let global = js_sys::global();
let mut browser = exists("window") && exists("document") && exists("location") && exists("navigator");
let process = Reflect::get(&global, &"process".into());
let versions = process
.clone()
.and_then(|process|Reflect::get(&process, &"versions".into()));
let nodejs = versions
.clone()
.map(|versions|exists_prop(&versions, "node")).unwrap_or(false);
let electron = versions
.clone()
.map(|versions|exists_prop(&versions, "electron")).unwrap_or(false);
if electron {
if let Ok(process_type) = process.and_then(|process|Reflect::get(&process, &"type".into())) {
browser = process_type.as_string().map(|v|v.as_str() == "renderer").unwrap_or(false);
}
}
let nwjs = Reflect::get(&global, &"nw".into())
.map(|nw|exists_prop(&nw, "Window")).unwrap_or(false);
let web = !nodejs && !nwjs && !electron;
JavaScriptRuntime {
nodejs,
nwjs,
electron,
browser,
web,
}
})
}
}
pub fn is_node() -> bool {
detect().nodejs
}
pub fn is_nw() -> bool {
detect().nwjs
}
pub fn is_electron() -> bool {
detect().electron
}
pub fn is_electron_server() -> bool {
detect().electron && !detect().browser
}
pub fn is_electron_client() -> bool {
detect().electron && detect().browser
}
pub fn is_web_capable() -> bool {
detect().browser
}
pub fn is_web()->bool{
detect().web
}
}else{
pub fn is_node() -> bool {
false
}
pub fn is_nw() -> bool {
false
}
pub fn is_electron() -> bool {
false
}
pub fn is_electron_server() -> bool {
false
}
pub fn is_electron_client() -> bool {
false
}
pub fn is_web_capable() -> bool {
false
}
pub fn is_web()->bool {
false
}
}
}
pub fn is_solana() -> bool {
cfg_if! {
if #[cfg(target_os = "solana")]{
true
}else{
false
}
}
}
pub fn is_wasm() -> bool {
cfg_if! {
if #[cfg(target_arch = "wasm32")]{
true
}else{
false
}
}
}
pub fn is_native() -> bool {
cfg_if! {
if #[cfg(any(target_os = "solana", target_arch = "wasm32"))] {
false
}else{
true
}
}
}
#[derive(Debug)]
pub enum Runtime {
Native,
Solana,
NW,
Node,
Web,
}
impl From<&Runtime> for String {
fn from(value: &Runtime) -> Self {
match value {
Runtime::Native => "Native",
Runtime::Solana => "Solana",
Runtime::NW => "NW",
Runtime::Node => "Node",
Runtime::Web => "Web",
}
.to_string()
}
}
impl std::fmt::Display for Runtime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str: String = self.into();
f.write_str(&str)
}
}
impl Runtime {
pub fn get() -> Self {
if is_solana() {
Runtime::Solana
} else if is_wasm() {
if is_nw() {
Runtime::NW
} else if is_node() {
Runtime::Node
} else {
Runtime::Web
}
} else {
Runtime::Native
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Platform {
Windows,
MacOS,
Linux,
FreeBSD,
OpenBSD,
NetBSD,
Android,
IOS,
Unknown,
Other(String),
}
impl Platform {
pub fn from_node() -> Self {
let process = js_sys::Reflect::get(&js_sys::global(), &"process".into())
.expect("Unable to get nodejs process global");
let platform = js_sys::Reflect::get(&process, &"platform".into())
.expect("Unable to get nodejs process.platform");
let platform = match platform
.as_string()
.expect("nodejs process.platform is not a string")
.as_str()
{
"win32" => Platform::Windows,
"darwin" => Platform::MacOS,
"linux" => Platform::Linux,
"openbsd" => Platform::OpenBSD,
"freebsd" => Platform::FreeBSD,
v => Platform::Other(v.to_string()),
};
platform
}
pub fn from_web() -> Self {
let window = if let Some(window) = web_sys::window() {
window
} else {
return Platform::Unknown;
};
let user_agent = if let Ok(user_agent) = window.navigator().user_agent() {
user_agent.to_lowercase()
} else {
return Platform::Unknown;
};
if user_agent.contains("win") {
Platform::Windows
} else if user_agent.contains("mac") {
Platform::MacOS
} else if user_agent.contains("linux") {
Platform::Linux
} else if user_agent.contains("android") {
Platform::Android
} else if user_agent.contains("ios")
|| user_agent.contains("iphone")
|| user_agent.contains("ipad")
{
Platform::IOS
} else if user_agent.contains("freebsd") {
Platform::FreeBSD
} else if user_agent.contains("openbsd") {
Platform::OpenBSD
} else if user_agent.contains("netbsd") {
Platform::NetBSD
} else {
Platform::Unknown
}
}
}
static mut PLATFORM: Option<Platform> = None;
pub fn platform() -> Platform {
if let Some(platform) = unsafe { PLATFORM.as_ref() } {
platform.clone()
} else {
cfg_if! {
if #[cfg(target_os = "windows")] {
let platform = Platform::Windows;
} else if #[cfg(target_os = "macos")] {
let platform = Platform::MacOS;
} else if #[cfg(target_os = "linux")] {
let platform = Platform::Linux;
} else if #[cfg(target_arch = "wasm32")] {
let platform = if is_node() {
Platform::from_node()
} else {
Platform::from_web()
};
}
}
unsafe { PLATFORM.replace(platform.clone()) };
platform
}
}
pub fn is_windows() -> bool {
cfg_if! {
if #[cfg(target_os = "windows")] {
true
} else {
platform() == Platform::Windows
}
}
}
pub fn is_macos() -> bool {
cfg_if! {
if #[cfg(target_os = "macos")] {
true
} else {
platform() == Platform::MacOS
}
}
}
pub fn is_linux() -> bool {
cfg_if! {
if #[cfg(target_os = "linux")] {
true
} else {
platform() == Platform::Linux
}
}
}
pub fn is_freebsd() -> bool {
cfg_if! {
if #[cfg(target_os = "freebsd")] {
true
} else {
platform() == Platform::FreeBSD
}
}
}
pub fn is_openbsd() -> bool {
cfg_if! {
if #[cfg(target_os = "openbsd")] {
true
} else {
platform() == Platform::OpenBSD
}
}
}
pub fn is_netbsd() -> bool {
cfg_if! {
if #[cfg(target_os = "netbsd")] {
true
} else {
platform() == Platform::NetBSD
}
}
}
pub fn is_ios() -> bool {
platform() == Platform::IOS
}
pub fn is_android() -> bool {
platform() == Platform::Android
}
pub fn is_unix() -> bool {
is_macos() || is_linux() || is_freebsd() || is_openbsd() || is_netbsd()
}
pub fn is_mobile() -> bool {
is_ios() || is_android()
}
pub fn is_chrome_extension() -> bool {
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
static mut IS_CHROME_EXTENSION : Option<bool> = None;
unsafe {
*IS_CHROME_EXTENSION.get_or_insert_with(||{
let global = js_sys::global();
let location = js_sys::Reflect::get(&global, &"location".into()).unwrap();
let protocol = js_sys::Reflect::get(&location, &"protocol".into()).unwrap();
protocol == "chrome-extension:"
})
}
} else {
false
}
}
}