#![allow(unknown_lints)]
#![allow(clippy::from_str_radix_10)]
#![deny(broken_intra_doc_links, invalid_html_tags)]
use bitflags::bitflags;
use std::fmt;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{collections::HashMap, time::Duration};
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};
pub trait FromRead: Sized {
fn from_read<R: Read>(r: R) -> ProcResult<Self>;
fn from_file<P: AsRef<Path>>(path: P) -> ProcResult<Self> {
std::fs::File::open(path.as_ref())
.map_err(|e| e.into())
.and_then(|f| Self::from_read(f))
.map_err(|e| e.error_path(path.as_ref()))
}
}
pub trait FromBufRead: Sized {
fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self>;
}
impl<T: FromBufRead> FromRead for T {
fn from_read<R: Read>(r: R) -> ProcResult<Self> {
Self::from_buf_read(BufReader::new(r))
}
}
pub trait FromReadSI: Sized {
fn from_read<R: Read>(r: R, system_info: &SystemInfo) -> ProcResult<Self>;
fn from_file<P: AsRef<Path>>(path: P, system_info: &SystemInfo) -> ProcResult<Self> {
std::fs::File::open(path.as_ref())
.map_err(|e| e.into())
.and_then(|f| Self::from_read(f, system_info))
.map_err(|e| e.error_path(path.as_ref()))
}
}
pub trait FromBufReadSI: Sized {
fn from_buf_read<R: BufRead>(r: R, system_info: &SystemInfo) -> ProcResult<Self>;
}
impl<T: FromBufReadSI> FromReadSI for T {
fn from_read<R: Read>(r: R, system_info: &SystemInfo) -> ProcResult<Self> {
Self::from_buf_read(BufReader::new(r), system_info)
}
}
pub mod prelude {
pub use super::{FromBufRead, FromBufReadSI, FromRead, FromReadSI};
}
#[doc(hidden)]
pub trait IntoOption<T> {
fn into_option(t: Self) -> Option<T>;
}
impl<T> IntoOption<T> for Option<T> {
fn into_option(t: Option<T>) -> Option<T> {
t
}
}
impl<T, R> IntoOption<T> for Result<T, R> {
fn into_option(t: Result<T, R>) -> Option<T> {
t.ok()
}
}
#[doc(hidden)]
pub trait IntoResult<T, E> {
fn into(t: Self) -> Result<T, E>;
}
#[macro_export]
#[doc(hidden)]
macro_rules! build_internal_error {
($err: expr) => {
crate::ProcError::InternalError(crate::InternalError {
msg: format!("Internal Unwrap Error: {}", $err),
file: file!(),
line: line!(),
#[cfg(feature = "backtrace")]
backtrace: backtrace::Backtrace::new(),
})
};
($err: expr, $msg: expr) => {
crate::ProcError::InternalError(crate::InternalError {
msg: format!("Internal Unwrap Error: {}: {}", $msg, $err),
file: file!(),
line: line!(),
#[cfg(feature = "backtrace")]
backtrace: backtrace::Backtrace::new(),
})
};
}
#[doc(hidden)]
pub struct NoneError;
impl std::fmt::Display for NoneError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NoneError")
}
}
impl<T> IntoResult<T, NoneError> for Option<T> {
fn into(t: Option<T>) -> Result<T, NoneError> {
t.ok_or(NoneError)
}
}
impl<T, E> IntoResult<T, E> for Result<T, E> {
fn into(t: Result<T, E>) -> Result<T, E> {
t
}
}
#[allow(unused_macros)]
#[macro_export]
#[doc(hidden)]
macro_rules! proc_panic {
($e:expr) => {
crate::IntoOption::into_option($e).unwrap_or_else(|| {
panic!(
"Failed to unwrap {}. Please report this as a procfs bug.",
stringify!($e)
)
})
};
($e:expr, $msg:expr) => {
crate::IntoOption::into_option($e).unwrap_or_else(|| {
panic!(
"Failed to unwrap {} ({}). Please report this as a procfs bug.",
stringify!($e),
$msg
)
})
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! expect {
($e:expr) => {
match crate::IntoResult::into($e) {
Ok(v) => v,
Err(e) => return Err(crate::build_internal_error!(e)),
}
};
($e:expr, $msg:expr) => {
match crate::IntoResult::into($e) {
Ok(v) => v,
Err(e) => return Err(crate::build_internal_error!(e, $msg)),
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! from_str {
($t:tt, $e:expr) => {{
let e = $e;
crate::expect!(
$t::from_str_radix(e, 10),
format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t),)
)
}};
($t:tt, $e:expr, $radix:expr) => {{
let e = $e;
crate::expect!(
$t::from_str_radix(e, $radix),
format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t))
)
}};
($t:tt, $e:expr, $radix:expr, pid:$pid:expr) => {{
let e = $e;
crate::expect!(
$t::from_str_radix(e, $radix),
format!(
"Failed to parse {} ({:?}) as a {} (pid {})",
stringify!($e),
e,
stringify!($t),
$pid
)
)
}};
}
pub trait SystemInfoInterface {
fn boot_time_secs(&self) -> ProcResult<u64>;
fn ticks_per_second(&self) -> u64;
fn page_size(&self) -> u64;
fn is_little_endian(&self) -> bool;
#[cfg(feature = "chrono")]
fn boot_time(&self) -> ProcResult<chrono::DateTime<chrono::Local>> {
use chrono::TimeZone;
let date_time = expect!(chrono::Local.timestamp_opt(self.boot_time_secs()? as i64, 0).single());
Ok(date_time)
}
}
pub type SystemInfo = dyn SystemInfoInterface;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct ExplicitSystemInfo {
pub boot_time_secs: u64,
pub ticks_per_second: u64,
pub page_size: u64,
pub is_little_endian: bool,
}
impl SystemInfoInterface for ExplicitSystemInfo {
fn boot_time_secs(&self) -> ProcResult<u64> {
Ok(self.boot_time_secs)
}
fn ticks_per_second(&self) -> u64 {
self.ticks_per_second
}
fn page_size(&self) -> u64 {
self.page_size
}
fn is_little_endian(&self) -> bool {
self.is_little_endian
}
}
pub trait WithSystemInfo<'a>: 'a {
type Output: 'a;
fn with_system_info(self, info: &SystemInfo) -> Self::Output;
}
impl<'a, F: 'a, R: 'a> WithSystemInfo<'a> for F
where
F: FnOnce(&SystemInfo) -> R,
{
type Output = R;
fn with_system_info(self, info: &SystemInfo) -> Self::Output {
self(info)
}
}
#[doc(hidden)]
pub fn from_iter<'a, I, U>(i: I) -> ProcResult<U>
where
I: IntoIterator<Item = &'a str>,
U: FromStr,
{
let mut iter = i.into_iter();
let val = expect!(iter.next());
match FromStr::from_str(val) {
Ok(u) => Ok(u),
Err(..) => Err(build_internal_error!("Failed to convert")),
}
}
fn from_iter_optional<'a, I, U>(i: I) -> ProcResult<Option<U>>
where
I: IntoIterator<Item = &'a str>,
U: FromStr,
{
let mut iter = i.into_iter();
let Some(val) = iter.next() else {
return Ok(None);
};
match FromStr::from_str(val) {
Ok(u) => Ok(Some(u)),
Err(..) => Err(build_internal_error!("Failed to convert")),
}
}
mod cgroups;
pub use cgroups::*;
mod cpuinfo;
pub use cpuinfo::*;
mod crypto;
pub use crypto::*;
mod devices;
pub use devices::*;
mod diskstats;
pub use diskstats::*;
mod iomem;
pub use iomem::*;
pub mod keyring;
mod locks;
pub use locks::*;
mod mounts;
pub use mounts::*;
mod partitions;
pub use partitions::*;
mod meminfo;
pub use meminfo::*;
pub mod net;
mod pressure;
pub use pressure::*;
pub mod process;
mod kpageflags;
pub use kpageflags::*;
pub mod sys;
pub use sys::kernel::Version as KernelVersion;
mod sysvipc_shm;
pub use sysvipc_shm::*;
mod uptime;
pub use uptime::*;
pub trait FromStrRadix: Sized {
fn from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>;
}
impl FromStrRadix for u64 {
fn from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError> {
u64::from_str_radix(s, radix)
}
}
impl FromStrRadix for i32 {
fn from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError> {
i32::from_str_radix(s, radix)
}
}
fn split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)> {
let mut s = s.split(sep);
let a = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
let b = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
Ok((a, b))
}
#[derive(Debug)]
#[doc(hidden)]
pub struct IoErrorWrapper {
pub path: PathBuf,
pub inner: std::io::Error,
}
impl std::error::Error for IoErrorWrapper {}
impl fmt::Display for IoErrorWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "IoErrorWrapper({}): {}", self.path.display(), self.inner)
}
}
pub type ProcResult<T> = Result<T, ProcError>;
#[derive(Debug)]
pub enum ProcError {
PermissionDenied(Option<PathBuf>),
NotFound(Option<PathBuf>),
Incomplete(Option<PathBuf>),
Io(std::io::Error, Option<PathBuf>),
Other(String),
InternalError(InternalError),
}
pub trait ProcErrorExt {
fn error_path(self, path: &Path) -> Self;
}
impl ProcErrorExt for ProcError {
fn error_path(mut self, path: &Path) -> Self {
use ProcError::*;
match &mut self {
PermissionDenied(p) | NotFound(p) | Incomplete(p) | Io(_, p) if p.is_none() => {
*p = Some(path.to_owned());
}
_ => (),
}
self
}
}
impl<T> ProcErrorExt for ProcResult<T> {
fn error_path(self, path: &Path) -> Self {
self.map_err(|e| e.error_path(path))
}
}
#[cfg_attr(feature = "serde1", derive(Serialize))]
pub struct InternalError {
pub msg: String,
pub file: &'static str,
pub line: u32,
#[cfg(feature = "backtrace")]
#[cfg_attr(feature = "serde1", serde(skip))]
pub backtrace: backtrace::Backtrace,
}
impl std::fmt::Debug for InternalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"bug at {}:{} (please report this procfs bug)\n{}",
self.file, self.line, self.msg
)
}
}
impl std::fmt::Display for InternalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"bug at {}:{} (please report this procfs bug)\n{}",
self.file, self.line, self.msg
)
}
}
impl From<std::io::Error> for ProcError {
fn from(io: std::io::Error) -> Self {
use std::io::ErrorKind;
let kind = io.kind();
if io.get_ref().is_some() {
let inner = io.into_inner().unwrap();
match inner.downcast::<IoErrorWrapper>() {
Ok(wrapper) => {
let path = wrapper.path;
match kind {
ErrorKind::PermissionDenied => ProcError::PermissionDenied(Some(path)),
ErrorKind::NotFound => ProcError::NotFound(Some(path)),
_other => {
const ESRCH: i32 = 3;
if matches!(wrapper.inner.raw_os_error(), Some(raw) if raw == ESRCH) {
return ProcError::NotFound(Some(path));
} else {
ProcError::Io(wrapper.inner, Some(path))
}
}
}
}
Err(io) => {
ProcError::Io(std::io::Error::new(kind, io), None)
}
}
} else {
match kind {
ErrorKind::PermissionDenied => ProcError::PermissionDenied(None),
ErrorKind::NotFound => ProcError::NotFound(None),
_other => ProcError::Io(io, None),
}
}
}
}
impl From<&'static str> for ProcError {
fn from(val: &'static str) -> Self {
ProcError::Other(val.to_owned())
}
}
impl From<std::num::ParseIntError> for ProcError {
fn from(val: std::num::ParseIntError) -> Self {
ProcError::Other(format!("ParseIntError: {}", val))
}
}
impl From<std::string::ParseError> for ProcError {
fn from(e: std::string::ParseError) -> Self {
match e {}
}
}
impl std::fmt::Display for ProcError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
ProcError::PermissionDenied(Some(p)) => write!(f, "Permission Denied: {}", p.display()),
ProcError::NotFound(Some(p)) => write!(f, "File not found: {}", p.display()),
ProcError::Incomplete(Some(p)) => write!(f, "Data incomplete: {}", p.display()),
ProcError::Io(inner, Some(p)) => {
write!(f, "Unexpected IO error({}): {}", p.display(), inner)
}
ProcError::PermissionDenied(None) => write!(f, "Permission Denied"),
ProcError::NotFound(None) => write!(f, "File not found"),
ProcError::Incomplete(None) => write!(f, "Data incomplete"),
ProcError::Io(inner, None) => write!(f, "Unexpected IO error: {}", inner),
ProcError::Other(s) => write!(f, "Unknown error {}", s),
ProcError::InternalError(e) => write!(f, "Internal error: {}", e),
}
}
}
impl std::error::Error for ProcError {}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct LoadAverage {
pub one: f32,
pub five: f32,
pub fifteen: f32,
pub cur: u32,
pub max: u32,
pub latest_pid: u32,
}
impl FromRead for LoadAverage {
fn from_read<R: Read>(mut reader: R) -> ProcResult<Self> {
let mut line = String::new();
reader.read_to_string(&mut line)?;
let mut s = line.split_whitespace();
let one = expect!(f32::from_str(expect!(s.next())));
let five = expect!(f32::from_str(expect!(s.next())));
let fifteen = expect!(f32::from_str(expect!(s.next())));
let curmax = expect!(s.next());
let latest_pid = expect!(u32::from_str(expect!(s.next())));
let mut s = curmax.split('/');
let cur = expect!(u32::from_str(expect!(s.next())));
let max = expect!(u32::from_str(expect!(s.next())));
Ok(LoadAverage {
one,
five,
fifteen,
cur,
max,
latest_pid,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum ConfigSetting {
Yes,
Module,
Value(String),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct KernelConfig(pub HashMap<String, ConfigSetting>);
impl FromBufRead for KernelConfig {
fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
let mut map = HashMap::new();
for line in r.lines() {
let line = line?;
if line.starts_with('#') {
continue;
}
if line.contains('=') {
let mut s = line.splitn(2, '=');
let name = expect!(s.next()).to_owned();
let value = match expect!(s.next()) {
"y" => ConfigSetting::Yes,
"m" => ConfigSetting::Module,
s => ConfigSetting::Value(s.to_owned()),
};
map.insert(name, value);
}
}
Ok(KernelConfig(map))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CpuTime {
pub user: u64,
pub nice: u64,
pub system: u64,
pub idle: u64,
pub iowait: Option<u64>,
pub irq: Option<u64>,
pub softirq: Option<u64>,
pub steal: Option<u64>,
pub guest: Option<u64>,
pub guest_nice: Option<u64>,
tps: u64,
}
impl CpuTime {
fn from_str(s: &str, ticks_per_second: u64) -> ProcResult<CpuTime> {
let mut s = s.split_whitespace();
let tps = ticks_per_second;
s.next();
let user = from_str!(u64, expect!(s.next()));
let nice = from_str!(u64, expect!(s.next()));
let system = from_str!(u64, expect!(s.next()));
let idle = from_str!(u64, expect!(s.next()));
let iowait = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
let irq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
let softirq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
let steal = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
let guest = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
let guest_nice = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
Ok(CpuTime {
user,
nice,
system,
idle,
iowait,
irq,
softirq,
steal,
guest,
guest_nice,
tps,
})
}
pub fn user_ms(&self) -> u64 {
let ms_per_tick = 1000 / self.tps;
self.user * ms_per_tick
}
pub fn user_duration(&self) -> Duration {
Duration::from_millis(self.user_ms())
}
pub fn nice_ms(&self) -> u64 {
let ms_per_tick = 1000 / self.tps;
self.nice * ms_per_tick
}
pub fn nice_duration(&self) -> Duration {
Duration::from_millis(self.nice_ms())
}
pub fn system_ms(&self) -> u64 {
let ms_per_tick = 1000 / self.tps;
self.system * ms_per_tick
}
pub fn system_duration(&self) -> Duration {
Duration::from_millis(self.system_ms())
}
pub fn idle_ms(&self) -> u64 {
let ms_per_tick = 1000 / self.tps;
self.idle * ms_per_tick
}
pub fn idle_duration(&self) -> Duration {
Duration::from_millis(self.idle_ms())
}
pub fn iowait_ms(&self) -> Option<u64> {
let ms_per_tick = 1000 / self.tps;
self.iowait.map(|io| io * ms_per_tick)
}
pub fn iowait_duration(&self) -> Option<Duration> {
self.iowait_ms().map(Duration::from_millis)
}
pub fn irq_ms(&self) -> Option<u64> {
let ms_per_tick = 1000 / self.tps;
self.irq.map(|ms| ms * ms_per_tick)
}
pub fn irq_duration(&self) -> Option<Duration> {
self.irq_ms().map(Duration::from_millis)
}
pub fn softirq_ms(&self) -> Option<u64> {
let ms_per_tick = 1000 / self.tps;
self.softirq.map(|ms| ms * ms_per_tick)
}
pub fn softirq_duration(&self) -> Option<Duration> {
self.softirq_ms().map(Duration::from_millis)
}
pub fn steal_ms(&self) -> Option<u64> {
let ms_per_tick = 1000 / self.tps;
self.steal.map(|ms| ms * ms_per_tick)
}
pub fn steal_duration(&self) -> Option<Duration> {
self.steal_ms().map(Duration::from_millis)
}
pub fn guest_ms(&self) -> Option<u64> {
let ms_per_tick = 1000 / self.tps;
self.guest.map(|ms| ms * ms_per_tick)
}
pub fn guest_duration(&self) -> Option<Duration> {
self.guest_ms().map(Duration::from_millis)
}
pub fn guest_nice_ms(&self) -> Option<u64> {
let ms_per_tick = 1000 / self.tps;
self.guest_nice.map(|ms| ms * ms_per_tick)
}
pub fn guest_nice_duration(&self) -> Option<Duration> {
self.guest_nice_ms().map(Duration::from_millis)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct KernelStats {
pub total: CpuTime,
pub cpu_time: Vec<CpuTime>,
pub ctxt: u64,
pub btime: u64,
pub processes: u64,
pub procs_running: Option<u32>,
pub procs_blocked: Option<u32>,
}
impl FromBufReadSI for KernelStats {
fn from_buf_read<R: BufRead>(r: R, system_info: &SystemInfo) -> ProcResult<Self> {
let lines = r.lines();
let mut total_cpu = None;
let mut cpus = Vec::new();
let mut ctxt = None;
let mut btime = None;
let mut processes = None;
let mut procs_running = None;
let mut procs_blocked = None;
for line in lines {
let line = line?;
if line.starts_with("cpu ") {
total_cpu = Some(CpuTime::from_str(&line, system_info.ticks_per_second())?);
} else if line.starts_with("cpu") {
cpus.push(CpuTime::from_str(&line, system_info.ticks_per_second())?);
} else if let Some(stripped) = line.strip_prefix("ctxt ") {
ctxt = Some(from_str!(u64, stripped));
} else if let Some(stripped) = line.strip_prefix("btime ") {
btime = Some(from_str!(u64, stripped));
} else if let Some(stripped) = line.strip_prefix("processes ") {
processes = Some(from_str!(u64, stripped));
} else if let Some(stripped) = line.strip_prefix("procs_running ") {
procs_running = Some(from_str!(u32, stripped));
} else if let Some(stripped) = line.strip_prefix("procs_blocked ") {
procs_blocked = Some(from_str!(u32, stripped));
}
}
Ok(KernelStats {
total: expect!(total_cpu),
cpu_time: cpus,
ctxt: expect!(ctxt),
btime: expect!(btime),
processes: expect!(processes),
procs_running,
procs_blocked,
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct VmStat(pub HashMap<String, i64>);
impl FromBufRead for VmStat {
fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
let mut map = HashMap::new();
for line in r.lines() {
let line = line?;
let mut split = line.split_whitespace();
let name = expect!(split.next());
let val = from_str!(i64, expect!(split.next()));
map.insert(name.to_owned(), val);
}
Ok(VmStat(map))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct KernelModule {
pub name: String,
pub size: u32,
pub refcount: i32,
pub used_by: Vec<String>,
pub state: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct KernelModules(pub HashMap<String, KernelModule>);
impl FromBufRead for KernelModules {
fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
let mut map = HashMap::new();
for line in r.lines() {
let line: String = line?;
let mut s = line.split_whitespace();
let name = expect!(s.next());
let size = from_str!(u32, expect!(s.next()));
let refcount = from_str!(i32, expect!(s.next()));
let used_by: &str = expect!(s.next());
let state = expect!(s.next());
map.insert(
name.to_string(),
KernelModule {
name: name.to_string(),
size,
refcount,
used_by: if used_by == "-" {
Vec::new()
} else {
used_by
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect()
},
state: state.to_string(),
},
);
}
Ok(KernelModules(map))
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct KernelCmdline(pub Vec<String>);
impl FromRead for KernelCmdline {
fn from_read<R: Read>(mut r: R) -> ProcResult<Self> {
let mut buf = String::new();
r.read_to_string(&mut buf)?;
Ok(KernelCmdline(
buf.split(' ')
.filter_map(|s| if !s.is_empty() { Some(s.to_string()) } else { None })
.collect(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kernel_from_str() {
let k = KernelVersion::from_str("1.2.3").unwrap();
assert_eq!(k.major, 1);
assert_eq!(k.minor, 2);
assert_eq!(k.patch, 3);
let k = KernelVersion::from_str("4.9.16-gentoo").unwrap();
assert_eq!(k.major, 4);
assert_eq!(k.minor, 9);
assert_eq!(k.patch, 16);
let k = KernelVersion::from_str("4.9.266-0.1.ac.225.84.332.metal1.x86_64").unwrap();
assert_eq!(k.major, 4);
assert_eq!(k.minor, 9);
assert_eq!(k.patch, 266);
}
#[test]
fn test_kernel_cmp() {
let a = KernelVersion::from_str("1.2.3").unwrap();
let b = KernelVersion::from_str("1.2.3").unwrap();
let c = KernelVersion::from_str("1.2.4").unwrap();
let d = KernelVersion::from_str("1.5.4").unwrap();
let e = KernelVersion::from_str("2.5.4").unwrap();
assert_eq!(a, b);
assert!(a < c);
assert!(a < d);
assert!(a < e);
assert!(e > d);
assert!(e > c);
assert!(e > b);
}
#[test]
fn test_loadavg_from_reader() -> ProcResult<()> {
let load_average = LoadAverage::from_read("2.63 1.00 1.42 3/4280 2496732".as_bytes())?;
assert_eq!(load_average.one, 2.63);
assert_eq!(load_average.five, 1.00);
assert_eq!(load_average.fifteen, 1.42);
assert_eq!(load_average.max, 4280);
assert_eq!(load_average.cur, 3);
assert_eq!(load_average.latest_pid, 2496732);
Ok(())
}
#[test]
fn test_from_str() -> ProcResult<()> {
assert_eq!(from_str!(u8, "12"), 12);
assert_eq!(from_str!(u8, "A", 16), 10);
Ok(())
}
#[test]
fn test_from_str_fail() {
fn inner() -> ProcResult<()> {
let s = "four";
from_str!(u8, s);
unreachable!()
}
assert!(inner().is_err())
}
#[test]
fn test_nopanic() {
fn _inner() -> ProcResult<bool> {
let x: Option<bool> = None;
let y: bool = expect!(x);
Ok(y)
}
let r = _inner();
println!("{:?}", r);
assert!(r.is_err());
fn _inner2() -> ProcResult<bool> {
let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
Ok(true)
}
let r = _inner2();
println!("{:?}", r);
assert!(r.is_err());
}
#[cfg(feature = "backtrace")]
#[test]
fn test_backtrace() {
fn _inner() -> ProcResult<bool> {
let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
Ok(true)
}
let r = _inner();
println!("{:?}", r);
}
}