Skip to main content

use_process_id/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, num::NonZeroU32};
5use std::error::Error;
6
7/// A non-zero numeric process identifier.
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct ProcessId(NonZeroU32);
10
11impl ProcessId {
12    /// Creates a process ID from a non-zero numeric value.
13    ///
14    /// # Errors
15    ///
16    /// Returns [`ProcessIdError::Zero`] when `value` is zero.
17    pub fn new(value: u32) -> Result<Self, ProcessIdError> {
18        NonZeroU32::new(value).map(Self).ok_or(ProcessIdError::Zero)
19    }
20
21    /// Returns the numeric process ID.
22    #[must_use]
23    pub const fn get(self) -> u32 {
24        self.0.get()
25    }
26}
27
28impl From<NonZeroU32> for ProcessId {
29    fn from(value: NonZeroU32) -> Self {
30        Self(value)
31    }
32}
33
34impl TryFrom<u32> for ProcessId {
35    type Error = ProcessIdError;
36
37    fn try_from(value: u32) -> Result<Self, Self::Error> {
38        Self::new(value)
39    }
40}
41
42impl fmt::Display for ProcessId {
43    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44        self.get().fmt(formatter)
45    }
46}
47
48/// A non-zero numeric parent process identifier.
49#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50pub struct ParentProcessId(NonZeroU32);
51
52impl ParentProcessId {
53    /// Creates a parent process ID from a non-zero numeric value.
54    ///
55    /// # Errors
56    ///
57    /// Returns [`ProcessIdError::Zero`] when `value` is zero.
58    pub fn new(value: u32) -> Result<Self, ProcessIdError> {
59        NonZeroU32::new(value).map(Self).ok_or(ProcessIdError::Zero)
60    }
61
62    /// Returns the numeric parent process ID.
63    #[must_use]
64    pub const fn get(self) -> u32 {
65        self.0.get()
66    }
67}
68
69impl From<NonZeroU32> for ParentProcessId {
70    fn from(value: NonZeroU32) -> Self {
71        Self(value)
72    }
73}
74
75impl TryFrom<u32> for ParentProcessId {
76    type Error = ProcessIdError;
77
78    fn try_from(value: u32) -> Result<Self, Self::Error> {
79        Self::new(value)
80    }
81}
82
83impl fmt::Display for ParentProcessId {
84    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85        self.get().fmt(formatter)
86    }
87}
88
89/// Errors returned while constructing process IDs.
90#[derive(Clone, Copy, Debug, Eq, PartialEq)]
91pub enum ProcessIdError {
92    /// Process ID zero is not accepted by these wrappers.
93    Zero,
94}
95
96impl fmt::Display for ProcessIdError {
97    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98        match self {
99            Self::Zero => formatter.write_str("process ID must be non-zero"),
100        }
101    }
102}
103
104impl Error for ProcessIdError {}
105
106#[cfg(test)]
107mod tests {
108    use super::{ParentProcessId, ProcessId, ProcessIdError};
109    use std::collections::BTreeSet;
110
111    #[test]
112    fn process_ids_reject_zero() {
113        assert_eq!(ProcessId::new(0), Err(ProcessIdError::Zero));
114        assert_eq!(ParentProcessId::new(0), Err(ProcessIdError::Zero));
115    }
116
117    #[test]
118    fn process_ids_store_numeric_values() {
119        let process_id = ProcessId::new(42).unwrap();
120
121        assert_eq!(process_id.get(), 42);
122        assert_eq!(process_id.to_string(), "42");
123    }
124
125    #[test]
126    fn parent_process_ids_store_numeric_values() {
127        let parent_process_id = ParentProcessId::try_from(7).unwrap();
128
129        assert_eq!(parent_process_id.get(), 7);
130        assert_eq!(parent_process_id.to_string(), "7");
131    }
132
133    #[test]
134    fn process_ids_have_deterministic_ordering() {
135        let mut process_ids = BTreeSet::new();
136
137        process_ids.insert(ProcessId::new(2).unwrap());
138        process_ids.insert(ProcessId::new(1).unwrap());
139
140        assert_eq!(
141            process_ids
142                .into_iter()
143                .map(ProcessId::get)
144                .collect::<Vec<_>>(),
145            vec![1, 2]
146        );
147    }
148}