joerl 0.7.1

An Erlang-inspired actor model library for Rust
Documentation
//! Process identifier (Pid) for actors.
//!
//! In Erlang terminology, a Pid uniquely identifies a process (actor).
//! This implementation uses a combination of node ID and sequence number
//! to ensure uniqueness even in distributed systems.

use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};

static PID_COUNTER: AtomicU64 = AtomicU64::new(1);

/// Process identifier that uniquely identifies an actor.
///
/// Pids are lightweight, copyable handles that can be sent between actors.
/// They follow Erlang naming conventions where a process is identified by
/// a unique Pid.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct Pid {
    /// Node identifier for distributed systems (0 for local)
    pub(crate) node: u32,
    /// Unique sequence number within the node
    pub(crate) id: u64,
}

impl Pid {
    /// Creates a new unique Pid for the local node.
    ///
    /// # Examples
    ///
    /// ```
    /// use joerl::Pid;
    ///
    /// let pid1 = Pid::new();
    /// let pid2 = Pid::new();
    /// assert_ne!(pid1, pid2);
    /// ```
    pub fn new() -> Self {
        Self {
            node: 0,
            id: PID_COUNTER.fetch_add(1, Ordering::Relaxed),
        }
    }

    /// Creates a Pid with a specific node and id.
    ///
    /// This is useful for distributed systems where Pids need to reference
    /// actors on different nodes.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use joerl::Pid;
    ///
    /// let remote_pid = Pid::with_node(42, 100);
    /// assert_eq!(remote_pid.node(), 42);
    /// assert_eq!(remote_pid.id(), 100);
    /// assert!(!remote_pid.is_local());
    /// ```
    pub fn with_node(node: u32, id: u64) -> Self {
        Self { node, id }
    }

    /// Returns true if this Pid represents a local actor.
    ///
    /// Local actors have node ID 0. This is useful for distributed
    /// systems where actors may exist on different nodes.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use joerl::Pid;
    ///
    /// let local_pid = Pid::new();
    /// assert!(local_pid.is_local());
    /// ```
    pub fn is_local(&self) -> bool {
        self.node == 0
    }

    /// Returns the node identifier.
    ///
    /// Node IDs are used in distributed systems. Local actors have node ID 0.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use joerl::Pid;
    ///
    /// let pid = Pid::new();
    /// assert_eq!(pid.node(), 0); // Local node
    /// ```
    pub fn node(&self) -> u32 {
        self.node
    }

    /// Returns the unique process identifier within this node.
    ///
    /// Each actor gets a unique, monotonically increasing ID.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use joerl::Pid;
    ///
    /// let pid1 = Pid::new();
    /// let pid2 = Pid::new();
    /// assert!(pid2.id() > pid1.id());
    /// ```
    pub fn id(&self) -> u64 {
        self.id
    }
}

impl Default for Pid {
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Display for Pid {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.node == 0 {
            write!(f, "<0.{}.0>", self.id)
        } else {
            write!(f, "<{}.{}.0>", self.node, self.id)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pid_unique() {
        let pid1 = Pid::new();
        let pid2 = Pid::new();
        assert_ne!(pid1, pid2);
    }

    #[test]
    fn test_pid_is_local() {
        let pid = Pid::new();
        assert!(pid.is_local());

        let remote_pid = Pid::with_node(1, 42);
        assert!(!remote_pid.is_local());
    }

    #[test]
    fn test_pid_display() {
        let pid = Pid::with_node(0, 123);
        assert_eq!(format!("{}", pid), "<0.123.0>");

        let remote_pid = Pid::with_node(2, 456);
        assert_eq!(format!("{}", remote_pid), "<2.456.0>");
    }

    #[test]
    fn test_pid_clone_copy() {
        let pid1 = Pid::new();
        let pid2 = pid1;
        assert_eq!(pid1, pid2);
    }

    #[test]
    fn test_pid_hash() {
        use std::collections::HashSet;
        let mut set = HashSet::new();
        let pid = Pid::new();
        set.insert(pid);
        assert!(set.contains(&pid));
    }
}