1use crate::node::node_name_atom;
17use serde::{Deserialize, Serialize};
18use starlang_atom::Atom;
19use std::fmt;
20use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
21
22static PID_COUNTER: AtomicU64 = AtomicU64::new(0);
24
25static CREATION: AtomicU32 = AtomicU32::new(0);
27
28#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
50pub struct Pid {
51 node: Atom,
53 id: u64,
55 creation: u32,
57}
58
59impl Pid {
60 pub fn new() -> Self {
75 Self {
76 node: node_name_atom(),
77 id: PID_COUNTER.fetch_add(1, Ordering::Relaxed),
78 creation: CREATION.load(Ordering::Relaxed),
79 }
80 }
81
82 pub fn from_parts_atom(node: Atom, id: u64, creation: u32) -> Self {
87 Self { node, id, creation }
88 }
89
90 pub fn remote(node_name: &str, id: u64, creation: u32) -> Self {
104 Self {
105 node: Atom::new(node_name),
106 id,
107 creation,
108 }
109 }
110
111 #[inline]
113 pub fn node(&self) -> Atom {
114 self.node
115 }
116
117 #[inline]
119 pub fn node_name(&self) -> String {
120 self.node.as_str()
121 }
122
123 #[inline]
125 pub const fn id(&self) -> u64 {
126 self.id
127 }
128
129 #[inline]
133 pub const fn creation(&self) -> u32 {
134 self.creation
135 }
136
137 #[inline]
141 pub fn is_local(&self) -> bool {
142 self.node == node_name_atom()
143 }
144}
145
146pub fn increment_creation() -> u32 {
151 CREATION.fetch_add(1, Ordering::SeqCst) + 1
152}
153
154pub fn current_creation() -> u32 {
156 CREATION.load(Ordering::Relaxed)
157}
158
159impl Default for Pid {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165impl fmt::Debug for Pid {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 if self.is_local() {
168 write!(f, "Pid<0.{}.{}>", self.id, self.creation)
169 } else {
170 write!(f, "Pid<{}.{}.{}>", self.node, self.id, self.creation)
172 }
173 }
174}
175
176impl fmt::Display for Pid {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 if self.is_local() {
179 write!(f, "<0.{}.{}>", self.id, self.creation)
180 } else {
181 write!(f, "<{}.{}.{}>", self.node, self.id, self.creation)
183 }
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use starlang_atom::atom;
191
192 #[test]
193 fn test_pid_uniqueness() {
194 let pid1 = Pid::new();
195 let pid2 = Pid::new();
196 assert_ne!(pid1, pid2);
197 }
198
199 #[test]
200 fn test_pid_local() {
201 let pid = Pid::new();
202 assert!(pid.is_local());
203 }
204
205 #[test]
206 fn test_pid_remote() {
207 let pid = Pid::remote("node2@localhost", 100, 2);
208 assert_eq!(pid.node_name(), "node2@localhost");
209 assert_eq!(pid.id(), 100);
210 assert_eq!(pid.creation(), 2);
211 assert!(!pid.is_local());
212 }
213
214 #[test]
215 fn test_pid_from_parts_atom() {
216 let node = atom!("test@host");
217 let pid = Pid::from_parts_atom(node, 42, 1);
218 assert_eq!(pid.node(), node);
219 assert_eq!(pid.id(), 42);
220 assert_eq!(pid.creation(), 1);
221 }
222
223 #[test]
224 fn test_pid_display_local() {
225 let pid = Pid::new();
226 let display = format!("{}", pid);
228 assert!(
229 display.starts_with("<0."),
230 "expected local display format, got: {}",
231 display
232 );
233 let parts: Vec<&str> = display
235 .trim_matches(|c| c == '<' || c == '>')
236 .split('.')
237 .collect();
238 assert_eq!(parts.len(), 3, "expected 3 parts in PID display");
239 assert_eq!(parts[0], "0", "expected node 0 for local PID");
240 }
241
242 #[test]
243 fn test_pid_display_remote() {
244 let pid = Pid::remote("node2@host", 42, 0);
245 assert_eq!(format!("{}", pid), "<node2@host.42.0>");
246 assert_eq!(format!("{:?}", pid), "Pid<node2@host.42.0>");
247 }
248
249 #[test]
250 fn test_pid_serialization() {
251 let pid = Pid::remote("node1@localhost", 123, 5);
252 let bytes = postcard::to_allocvec(&pid).unwrap();
253 let decoded: Pid = postcard::from_bytes(&bytes).unwrap();
254 assert_eq!(pid, decoded);
255 assert_eq!(decoded.node_name(), "node1@localhost");
256 }
257
258 #[test]
259 fn test_pid_hash() {
260 use std::collections::HashSet;
261
262 let mut set = HashSet::new();
263 let pid1 = Pid::new();
264 let pid2 = Pid::new();
265
266 set.insert(pid1);
267 set.insert(pid2);
268 set.insert(pid1); assert_eq!(set.len(), 2);
271 }
272
273 #[test]
274 fn test_creation_distinguishes_pids() {
275 let node = atom!("test@host");
277 let pid1 = Pid::from_parts_atom(node, 42, 0);
278 let pid2 = Pid::from_parts_atom(node, 42, 1);
279 assert_ne!(pid1, pid2);
280 }
281
282 #[test]
283 fn test_increment_creation() {
284 let before = current_creation();
285 let new_creation = increment_creation();
286 assert_eq!(new_creation, before + 1);
287 assert_eq!(current_creation(), new_creation);
288 }
289}