1use std::{fmt, num::NonZeroU32, process::Child};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct ProcessId(NonZeroU32);
13
14impl ProcessId {
15 pub const fn new(pid: u32) -> Option<Self> {
17 match NonZeroU32::new(pid) {
18 Some(pid) => Some(Self(pid)),
19 None => None,
20 }
21 }
22
23 pub fn current() -> Self {
25 Self::new(std::process::id()).expect("a running process has a nonzero PID")
26 }
27
28 pub fn of(child: &Child) -> Self {
30 Self::new(child.id()).expect("a spawned child has a nonzero PID")
31 }
32
33 pub const fn get(self) -> u32 {
35 self.0.get()
36 }
37}
38
39impl fmt::Display for ProcessId {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.0.fmt(f)
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn pid_zero_is_not_a_process() {
51 assert_eq!(ProcessId::new(0), None);
52 assert_eq!(ProcessId::new(7).unwrap().get(), 7);
53 assert_eq!(ProcessId::new(7).unwrap().to_string(), "7");
54 }
55
56 #[test]
57 fn current_and_child_pids_come_from_the_os() {
58 assert_eq!(ProcessId::current().get(), std::process::id());
59 let mut child = std::process::Command::new(std::env::current_exe().unwrap())
60 .arg("--list")
61 .stdout(std::process::Stdio::null())
62 .spawn()
63 .unwrap();
64 assert_eq!(ProcessId::of(&child).get(), child.id());
65 child.wait().unwrap();
66 }
67}