#![warn(unused_results)]
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fmt::{Debug, Formatter};
use crate::{PlatformInfoAPI, PlatformInfoError, UNameAPI};
use unix_safe::{oss_from_cstr, utsname};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PlatformInfo {
pub utsname: UTSName,
sysname: OsString,
nodename: OsString,
release: OsString,
version: OsString,
machine: OsString,
processor: OsString,
osname: OsString,
}
impl PlatformInfoAPI for PlatformInfo {
fn new() -> Result<Self, PlatformInfoError> {
let utsname = UTSName(utsname()?);
let machine = oss_from_cstr(&utsname.0.machine);
let processor = OsString::from(crate::lib_impl::map_processor(&machine.to_string_lossy()));
Ok(Self {
utsname,
sysname: oss_from_cstr(&utsname.0.sysname),
nodename: oss_from_cstr(&utsname.0.nodename),
release: oss_from_cstr(&utsname.0.release),
version: oss_from_cstr(&utsname.0.version),
machine,
processor,
osname: OsString::from(crate::lib_impl::HOST_OS_NAME),
})
}
}
impl UNameAPI for PlatformInfo {
fn sysname(&self) -> &OsStr {
&self.sysname
}
fn nodename(&self) -> &OsStr {
&self.nodename
}
fn release(&self) -> &OsStr {
&self.release
}
fn version(&self) -> &OsStr {
&self.version
}
fn machine(&self) -> &OsStr {
&self.machine
}
fn processor(&self) -> &OsStr {
&self.processor
}
fn osname(&self) -> &OsStr {
&self.osname
}
}
#[derive(Clone, Copy /* , Debug, PartialEq, Eq */)]
pub struct UTSName(libc::utsname);
impl Debug for UTSName {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut debug_struct = &mut f.debug_struct("UTSName");
debug_struct = debug_struct
.field("sysname", &oss_from_cstr(&self.0.sysname))
.field("nodename", &oss_from_cstr(&self.0.nodename))
.field("release", &oss_from_cstr(&self.0.release))
.field("version", &oss_from_cstr(&self.0.version))
.field("machine", &oss_from_cstr(&self.0.machine));
#[cfg(not(any(
target_os = "aix",
target_os = "illumos",
target_os = "solaris",
target_os = "macos",
target_os = "ios",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "haiku"
)))]
{
debug_struct = debug_struct.field("domainname", &oss_from_cstr(&self.0.domainname));
}
debug_struct.finish()
}
}
impl PartialEq for UTSName {
fn eq(&self, other: &Self) -> bool {
let mut equal = true; equal = equal
&& (
self.0.sysname,
self.0.nodename,
self.0.release,
self.0.version,
self.0.machine,
) == (
other.0.sysname,
other.0.nodename,
other.0.release,
other.0.version,
other.0.machine,
);
#[cfg(not(any(
target_os = "aix",
target_os = "illumos",
target_os = "solaris",
target_os = "macos",
target_os = "ios",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "haiku"
)))]
{
equal = equal && (self.0.domainname == other.0.domainname);
}
equal
}
}
impl Eq for UTSName {}
mod unix_safe {
use std::convert::TryFrom;
use std::ffi::{CStr, OsStr, OsString};
use std::io;
use std::mem::MaybeUninit;
use std::os::unix::ffi::OsStrExt;
pub fn oss_from_cstr(slice: &[libc::c_char]) -> OsString {
assert!(slice.len() < usize::try_from(isize::MAX).unwrap());
assert!(slice.iter().position(|&c| c == 0 ).unwrap() < slice.len());
OsString::from(OsStr::from_bytes(
unsafe { CStr::from_ptr(slice.as_ptr()) }.to_bytes(),
))
}
pub fn utsname() -> Result<libc::utsname, std::io::Error> {
let mut uts = MaybeUninit::<libc::utsname>::uninit();
let result = unsafe { libc::uname(uts.as_mut_ptr()) };
if result != -1 {
Ok(unsafe { uts.assume_init() })
} else {
Err(io::Error::last_os_error())
}
}
}
#[test]
fn test_osname() {
let info = PlatformInfo::new().unwrap();
let osname = info.osname().to_string_lossy();
assert!(osname.starts_with(crate::lib_impl::HOST_OS_NAME));
}
#[test]
fn test_processor() {
let info = PlatformInfo::new().unwrap();
let processor = info.processor().to_string_lossy();
assert!(!processor.is_empty());
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
assert_eq!(processor, "arm", "macOS arm64 should map to 'arm'");
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
assert_eq!(processor, "aarch64", "Linux aarch64 should pass through");
#[cfg(target_arch = "x86_64")]
assert_eq!(processor, "x86_64", "x86_64 should pass through");
#[cfg(target_arch = "x86")]
assert_eq!(processor, "i686", "x86 variants should normalize to i686");
}
#[test]
fn structure_clone() {
let info = PlatformInfo::new().unwrap();
println!("{info:?}");
#[allow(clippy::redundant_clone)] let info_copy = info.clone();
assert_eq!(info_copy, info);
}