1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, num::NonZeroU32};
5use std::error::Error;
6
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct ProcessId(NonZeroU32);
10
11impl ProcessId {
12 pub fn new(value: u32) -> Result<Self, ProcessIdError> {
18 NonZeroU32::new(value).map(Self).ok_or(ProcessIdError::Zero)
19 }
20
21 #[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#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50pub struct ParentProcessId(NonZeroU32);
51
52impl ParentProcessId {
53 pub fn new(value: u32) -> Result<Self, ProcessIdError> {
59 NonZeroU32::new(value).map(Self).ok_or(ProcessIdError::Zero)
60 }
61
62 #[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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
91pub enum ProcessIdError {
92 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}