use std::io::Read;
use std::io::{self};
use super::FromRead;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcState {
Running,
Sleeping,
Waiting,
Zombie,
Stopped,
Tracing,
Dead,
Wakekill,
Waking,
Parked,
Idle,
Other(char),
}
impl From<char> for ProcState {
fn from(c: char) -> Self {
match c {
'R' => Self::Running,
'S' => Self::Sleeping,
'D' => Self::Waiting,
'Z' => Self::Zombie,
'T' => Self::Stopped,
't' => Self::Tracing,
'X' | 'x' => Self::Dead,
'K' => Self::Wakekill,
'W' => Self::Waking,
'P' => Self::Parked,
'I' => Self::Idle,
other => Self::Other(other),
}
}
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct Stat {
pub state: Option<ProcState>,
pub ppid: Option<u64>,
pub pgrp: Option<u64>,
pub sid: Option<u64>,
pub utime: Option<u64>,
pub stime: Option<u64>,
pub cutime: Option<u64>,
pub cstime: Option<u64>,
pub nice: Option<i32>,
pub num_threads: Option<u64>,
pub starttime: Option<u64>,
pub processor: Option<u32>,
}
impl FromRead for Stat {
fn from_read(reader: impl Read) -> io::Result<Self> {
const MAX_STAT_BYTES: u64 = 4096;
let mut buf = String::new();
reader.take(MAX_STAT_BYTES).read_to_string(&mut buf)?;
let comm_end = buf.rfind(')').ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
"missing closing ')' for comm field in stat",
)
})?;
let after_comm = buf[comm_end + 1..].trim();
let fields: Vec<&str> = after_comm.split_whitespace().collect();
fn try_parse<T: std::str::FromStr>(fields: &[&str], idx: usize) -> Option<T> {
let s = *fields.get(idx)?;
match s.parse::<T>() {
Ok(v) => Some(v),
Err(_) => {
eprintln!("warning: stat: could not parse field {idx}: '{s}'");
None
}
}
}
Ok(Stat {
state: fields
.first()
.and_then(|s| s.chars().next())
.map(ProcState::from),
ppid: try_parse(&fields, 1),
pgrp: try_parse(&fields, 2),
sid: try_parse(&fields, 3),
utime: try_parse(&fields, 11),
stime: try_parse(&fields, 12),
cutime: try_parse(&fields, 13),
cstime: try_parse(&fields, 14),
nice: try_parse(&fields, 16),
num_threads: try_parse(&fields, 17),
starttime: try_parse(&fields, 19),
processor: try_parse(&fields, 36),
})
}
}