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}