Skip to main content

peek_proc_reader/
error.rs

1//! Typed errors for proc-reader so consumers can match on failure modes.
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum ProcReaderError {
8    #[error("process {0} not found")]
9    NotFound(i32),
10
11    #[error("I/O error reading {path}: {source}")]
12    Io {
13        path: PathBuf,
14        #[source]
15        source: std::io::Error,
16    },
17
18    #[error("parse error in {path}: {msg}")]
19    Parse { path: PathBuf, msg: String },
20}
21
22pub type Result<T> = std::result::Result<T, ProcReaderError>;
23
24impl ProcReaderError {
25    /// PID involved in the error, if known (from NotFound or from path like /proc/123/...).
26    pub fn pid(&self) -> Option<i32> {
27        match self {
28            ProcReaderError::NotFound(pid) => Some(*pid),
29            ProcReaderError::Io { path, .. } => pid_from_proc_path(path),
30            ProcReaderError::Parse { path, .. } => pid_from_proc_path(path),
31        }
32    }
33}
34
35/// Extract PID from a path like `/proc/123/limits` or `/proc/123/fd`. Returns None if not /proc/<pid>/...
36pub(crate) fn pid_from_proc_path(path: &std::path::Path) -> Option<i32> {
37    let mut components = path.components();
38    let _root = components.next()?;
39    let proc_name = components.next()?.as_os_str().to_str()?;
40    if proc_name != "proc" {
41        return None;
42    }
43    let pid_str = components.next()?.as_os_str().to_str()?;
44    pid_str.parse().ok()
45}
46
47/// Map io::Error to ProcReaderError; use path and optional pid for context.
48pub(crate) fn io_to_error(path: PathBuf, e: std::io::Error, pid: i32) -> ProcReaderError {
49    if e.kind() == std::io::ErrorKind::NotFound {
50        ProcReaderError::NotFound(pid)
51    } else {
52        ProcReaderError::Io { path, source: e }
53    }
54}