use crate::Pid;
use crate::error::{ActorError, Result};
use dashmap::DashMap;
use std::sync::Arc;
#[cfg(feature = "telemetry")]
use crate::telemetry::RegistryMetrics;
#[derive(Default)]
pub struct Registry {
names: Arc<DashMap<String, Pid>>,
pids: Arc<DashMap<Pid, String>>,
}
impl Registry {
pub fn new() -> Self {
Self {
names: Arc::new(DashMap::new()),
pids: Arc::new(DashMap::new()),
}
}
pub fn register(&self, name: impl Into<String>, pid: Pid) -> Result<()> {
let name = name.into();
if name.is_empty() {
return Err(ActorError::InvalidName("Name cannot be empty".to_string()));
}
if let Some(old_name) = self.pids.get(&pid)
&& old_name.value() != &name
{
self.names.remove(old_name.value());
}
match self.names.entry(name.clone()) {
dashmap::mapref::entry::Entry::Occupied(_) => {
#[cfg(feature = "telemetry")]
RegistryMetrics::registration_conflict();
Err(ActorError::NameAlreadyRegistered(name))
}
dashmap::mapref::entry::Entry::Vacant(entry) => {
entry.insert(pid);
self.pids.insert(pid, name.clone());
#[cfg(feature = "telemetry")]
RegistryMetrics::process_registered();
Ok(())
}
}
}
pub fn unregister(&self, name: &str) -> Result<Pid> {
if let Some((_, pid)) = self.names.remove(name) {
self.pids.remove(&pid);
#[cfg(feature = "telemetry")]
RegistryMetrics::process_unregistered();
Ok(pid)
} else {
Err(ActorError::NameNotRegistered(name.to_string()))
}
}
pub fn whereis(&self, name: &str) -> Option<Pid> {
#[cfg(feature = "telemetry")]
RegistryMetrics::lookup_performed();
self.names.get(name).map(|entry| *entry.value())
}
pub fn registered(&self) -> Vec<String> {
self.names.iter().map(|entry| entry.key().clone()).collect()
}
pub fn name_of(&self, pid: Pid) -> Option<String> {
self.pids.get(&pid).map(|entry| entry.value().clone())
}
pub fn len(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub(crate) fn cleanup_pid(&self, pid: Pid) {
if let Some((_, name)) = self.pids.remove(&pid) {
self.names.remove(&name);
#[cfg(feature = "telemetry")]
RegistryMetrics::process_unregistered();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_and_whereis() {
let registry = Registry::new();
let pid = Pid::new();
registry.register("test", pid).unwrap();
assert_eq!(registry.whereis("test"), Some(pid));
}
#[test]
fn test_register_duplicate_name() {
let registry = Registry::new();
let pid1 = Pid::new();
let pid2 = Pid::new();
registry.register("test", pid1).unwrap();
let result = registry.register("test", pid2);
assert!(matches!(result, Err(ActorError::NameAlreadyRegistered(_))));
}
#[test]
fn test_register_same_pid_different_name() {
let registry = Registry::new();
let pid = Pid::new();
registry.register("name1", pid).unwrap();
registry.register("name2", pid).unwrap();
assert_eq!(registry.whereis("name1"), None);
assert_eq!(registry.whereis("name2"), Some(pid));
assert_eq!(registry.name_of(pid), Some("name2".to_string()));
}
#[test]
fn test_unregister() {
let registry = Registry::new();
let pid = Pid::new();
registry.register("test", pid).unwrap();
let unregistered_pid = registry.unregister("test").unwrap();
assert_eq!(unregistered_pid, pid);
assert_eq!(registry.whereis("test"), None);
}
#[test]
fn test_unregister_not_registered() {
let registry = Registry::new();
let result = registry.unregister("nonexistent");
assert!(matches!(result, Err(ActorError::NameNotRegistered(_))));
}
#[test]
fn test_registered() {
let registry = Registry::new();
let pid1 = Pid::new();
let pid2 = Pid::new();
registry.register("process1", pid1).unwrap();
registry.register("process2", pid2).unwrap();
let names = registry.registered();
assert_eq!(names.len(), 2);
assert!(names.contains(&"process1".to_string()));
assert!(names.contains(&"process2".to_string()));
}
#[test]
fn test_name_of() {
let registry = Registry::new();
let pid = Pid::new();
registry.register("my_process", pid).unwrap();
assert_eq!(registry.name_of(pid), Some("my_process".to_string()));
}
#[test]
fn test_cleanup_pid() {
let registry = Registry::new();
let pid = Pid::new();
registry.register("test", pid).unwrap();
registry.cleanup_pid(pid);
assert_eq!(registry.whereis("test"), None);
assert_eq!(registry.name_of(pid), None);
}
#[test]
fn test_empty_name() {
let registry = Registry::new();
let pid = Pid::new();
let result = registry.register("", pid);
assert!(matches!(result, Err(ActorError::InvalidName(_))));
}
#[test]
fn test_len_and_is_empty() {
let registry = Registry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
let pid = Pid::new();
registry.register("test", pid).unwrap();
assert!(!registry.is_empty());
assert_eq!(registry.len(), 1);
registry.unregister("test").unwrap();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
}