joerl/pid.rs
1//! Process identifier (Pid) for actors.
2//!
3//! In Erlang terminology, a Pid uniquely identifies a process (actor).
4//! This implementation uses a combination of node ID and sequence number
5//! to ensure uniqueness even in distributed systems.
6
7use std::fmt;
8use std::sync::atomic::{AtomicU64, Ordering};
9
10static PID_COUNTER: AtomicU64 = AtomicU64::new(1);
11
12/// Process identifier that uniquely identifies an actor.
13///
14/// Pids are lightweight, copyable handles that can be sent between actors.
15/// They follow Erlang naming conventions where a process is identified by
16/// a unique Pid.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
18pub struct Pid {
19 /// Node identifier for distributed systems (0 for local)
20 pub(crate) node: u32,
21 /// Unique sequence number within the node
22 pub(crate) id: u64,
23}
24
25impl Pid {
26 /// Creates a new unique Pid for the local node.
27 ///
28 /// # Examples
29 ///
30 /// ```
31 /// use joerl::Pid;
32 ///
33 /// let pid1 = Pid::new();
34 /// let pid2 = Pid::new();
35 /// assert_ne!(pid1, pid2);
36 /// ```
37 pub fn new() -> Self {
38 Self {
39 node: 0,
40 id: PID_COUNTER.fetch_add(1, Ordering::Relaxed),
41 }
42 }
43
44 /// Creates a Pid with a specific node and id.
45 ///
46 /// This is useful for distributed systems where Pids need to reference
47 /// actors on different nodes.
48 ///
49 /// # Examples
50 ///
51 /// ```rust
52 /// use joerl::Pid;
53 ///
54 /// let remote_pid = Pid::with_node(42, 100);
55 /// assert_eq!(remote_pid.node(), 42);
56 /// assert_eq!(remote_pid.id(), 100);
57 /// assert!(!remote_pid.is_local());
58 /// ```
59 pub fn with_node(node: u32, id: u64) -> Self {
60 Self { node, id }
61 }
62
63 /// Returns true if this Pid represents a local actor.
64 ///
65 /// Local actors have node ID 0. This is useful for distributed
66 /// systems where actors may exist on different nodes.
67 ///
68 /// # Examples
69 ///
70 /// ```rust
71 /// use joerl::Pid;
72 ///
73 /// let local_pid = Pid::new();
74 /// assert!(local_pid.is_local());
75 /// ```
76 pub fn is_local(&self) -> bool {
77 self.node == 0
78 }
79
80 /// Returns the node identifier.
81 ///
82 /// Node IDs are used in distributed systems. Local actors have node ID 0.
83 ///
84 /// # Examples
85 ///
86 /// ```rust
87 /// use joerl::Pid;
88 ///
89 /// let pid = Pid::new();
90 /// assert_eq!(pid.node(), 0); // Local node
91 /// ```
92 pub fn node(&self) -> u32 {
93 self.node
94 }
95
96 /// Returns the unique process identifier within this node.
97 ///
98 /// Each actor gets a unique, monotonically increasing ID.
99 ///
100 /// # Examples
101 ///
102 /// ```rust
103 /// use joerl::Pid;
104 ///
105 /// let pid1 = Pid::new();
106 /// let pid2 = Pid::new();
107 /// assert!(pid2.id() > pid1.id());
108 /// ```
109 pub fn id(&self) -> u64 {
110 self.id
111 }
112}
113
114impl Default for Pid {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120impl fmt::Display for Pid {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 if self.node == 0 {
123 write!(f, "<0.{}.0>", self.id)
124 } else {
125 write!(f, "<{}.{}.0>", self.node, self.id)
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_pid_unique() {
136 let pid1 = Pid::new();
137 let pid2 = Pid::new();
138 assert_ne!(pid1, pid2);
139 }
140
141 #[test]
142 fn test_pid_is_local() {
143 let pid = Pid::new();
144 assert!(pid.is_local());
145
146 let remote_pid = Pid::with_node(1, 42);
147 assert!(!remote_pid.is_local());
148 }
149
150 #[test]
151 fn test_pid_display() {
152 let pid = Pid::with_node(0, 123);
153 assert_eq!(format!("{}", pid), "<0.123.0>");
154
155 let remote_pid = Pid::with_node(2, 456);
156 assert_eq!(format!("{}", remote_pid), "<2.456.0>");
157 }
158
159 #[test]
160 fn test_pid_clone_copy() {
161 let pid1 = Pid::new();
162 let pid2 = pid1;
163 assert_eq!(pid1, pid2);
164 }
165
166 #[test]
167 fn test_pid_hash() {
168 use std::collections::HashSet;
169 let mut set = HashSet::new();
170 let pid = Pid::new();
171 set.insert(pid);
172 assert!(set.contains(&pid));
173 }
174}