use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Os {
Windows,
Linux,
MacOS,
FreeBSD,
OpenBSD,
NetBSD,
Solaris,
Android,
Ios,
Unknown,
}
impl fmt::Display for Os {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Os::Windows => write!(f, "Windows"),
Os::Linux => write!(f, "Linux"),
Os::MacOS => write!(f, "macOS"),
Os::FreeBSD => write!(f, "FreeBSD"),
Os::OpenBSD => write!(f, "OpenBSD"),
Os::NetBSD => write!(f, "NetBSD"),
Os::Solaris => write!(f, "Solaris"),
Os::Android => write!(f, "Android"),
Os::Ios => write!(f, "iOS"),
Os::Unknown => write!(f, "Unknown"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseOsError(String);
impl fmt::Display for ParseOsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unknown operating system: '{}'", self.0)
}
}
impl std::error::Error for ParseOsError {}
impl FromStr for Os {
type Err = ParseOsError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"windows" | "win" | "win32" | "win64" => Ok(Os::Windows),
"linux" | "gnu/linux" => Ok(Os::Linux),
"macos" | "darwin" | "osx" | "mac" => Ok(Os::MacOS),
"freebsd" => Ok(Os::FreeBSD),
"openbsd" => Ok(Os::OpenBSD),
"netbsd" => Ok(Os::NetBSD),
"solaris" | "sunos" => Ok(Os::Solaris),
"android" => Ok(Os::Android),
"ios" => Ok(Os::Ios),
_ => Err(ParseOsError(s.to_string())),
}
}
}
impl Os {
pub fn parse(s: &str) -> Option<Os> {
s.parse().ok()
}
pub fn is_unix_like(&self) -> bool {
matches!(
self,
Os::Linux | Os::MacOS | Os::FreeBSD | Os::OpenBSD | Os::NetBSD | Os::Solaris | Os::Android
)
}
pub fn is_bsd(&self) -> bool {
matches!(self, Os::FreeBSD | Os::OpenBSD | Os::NetBSD | Os::MacOS)
}
pub fn all() -> &'static [Os] {
&[
Os::Windows,
Os::Linux,
Os::MacOS,
Os::FreeBSD,
Os::OpenBSD,
Os::NetBSD,
Os::Solaris,
Os::Android,
Os::Ios,
]
}
}
#[cfg(target_os = "windows")]
pub fn detect_os() -> Os {
Os::Windows
}
#[cfg(target_os = "linux")]
pub fn detect_os() -> Os {
if std::path::Path::new("/system/build.prop").exists() {
return Os::Android;
}
Os::Linux
}
#[cfg(target_os = "macos")]
pub fn detect_os() -> Os {
Os::MacOS
}
#[cfg(target_os = "freebsd")]
pub fn detect_os() -> Os {
Os::FreeBSD
}
#[cfg(target_os = "openbsd")]
pub fn detect_os() -> Os {
Os::OpenBSD
}
#[cfg(target_os = "netbsd")]
pub fn detect_os() -> Os {
Os::NetBSD
}
#[cfg(target_os = "solaris")]
pub fn detect_os() -> Os {
Os::Solaris
}
#[cfg(target_os = "ios")]
pub fn detect_os() -> Os {
Os::Ios
}
#[cfg(target_os = "android")]
pub fn detect_os() -> Os {
Os::Android
}
#[cfg(not(any(
target_os = "windows",
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "solaris",
target_os = "ios",
target_os = "android"
)))]
pub fn detect_os() -> Os {
Os::Unknown
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_os_from_str() {
assert_eq!("windows".parse::<Os>(), Ok(Os::Windows));
assert_eq!("Windows".parse::<Os>(), Ok(Os::Windows));
assert_eq!("WINDOWS".parse::<Os>(), Ok(Os::Windows));
assert_eq!("win".parse::<Os>(), Ok(Os::Windows));
assert_eq!("linux".parse::<Os>(), Ok(Os::Linux));
assert_eq!("macos".parse::<Os>(), Ok(Os::MacOS));
assert_eq!("darwin".parse::<Os>(), Ok(Os::MacOS));
assert_eq!("freebsd".parse::<Os>(), Ok(Os::FreeBSD));
assert!("invalid".parse::<Os>().is_err());
}
#[test]
fn test_os_parse() {
assert_eq!(Os::parse("windows"), Some(Os::Windows));
assert_eq!(Os::parse("invalid"), None);
}
#[test]
fn test_os_is_unix_like() {
assert!(!Os::Windows.is_unix_like());
assert!(Os::Linux.is_unix_like());
assert!(Os::MacOS.is_unix_like());
assert!(Os::FreeBSD.is_unix_like());
}
#[test]
fn test_os_is_bsd() {
assert!(!Os::Windows.is_bsd());
assert!(!Os::Linux.is_bsd());
assert!(Os::MacOS.is_bsd());
assert!(Os::FreeBSD.is_bsd());
assert!(Os::OpenBSD.is_bsd());
assert!(Os::NetBSD.is_bsd());
}
#[test]
fn test_os_display() {
assert_eq!(format!("{}", Os::Windows), "Windows");
assert_eq!(format!("{}", Os::Linux), "Linux");
assert_eq!(format!("{}", Os::MacOS), "macOS");
}
#[test]
fn test_detect_os() {
let os = detect_os();
assert!(Os::all().contains(&os) || os == Os::Unknown);
}
}