use derive_where::derive_where;
use slotmap_fork_lmondada::{new_key_type, SlotMap};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::RelWeak;
use crate::{node::InnerData, RelRc};
new_key_type! {
pub struct NodeId;
}
#[derive(Debug)]
#[derive_where(Clone, Default)]
pub struct Registry<N, E> {
nodes: SlotMap<NodeId, RelWeak<N, E>>,
ptr_to_id: HashMap<*const InnerData<N, E>, NodeId>,
}
impl<N, E> Registry<N, E> {
pub fn new() -> Self {
Self {
nodes: SlotMap::with_key(),
ptr_to_id: HashMap::new(),
}
}
pub(crate) fn from_slotmap(nodes: SlotMap<NodeId, RelWeak<N, E>>) -> Self {
let ptr_to_id = nodes
.iter()
.map(|(id, weak_ref)| (weak_ref.as_ptr(), id))
.collect();
Self { nodes, ptr_to_id }
}
pub fn add_node(&mut self, node: &RelRc<N, E>) -> NodeId {
if let Some(existing_id) = self.get_id(node) {
return existing_id; }
let weak_ref = node.downgrade();
let id = self.nodes.insert(weak_ref);
self.ptr_to_id.insert(node.as_ptr(), id);
id
}
pub fn get_id(&self, node: &RelRc<N, E>) -> Option<NodeId> {
self.ptr_to_id.get(&node.as_ptr()).copied()
}
pub fn get_id_or_insert(&mut self, node: &RelRc<N, E>) -> NodeId {
if let Some(id) = self.get_id(node) {
id
} else {
self.add_node(node)
}
}
pub fn contains(&self, node: &RelRc<N, E>) -> bool {
self.get_id(node).is_some()
}
pub fn get(&self, id: NodeId) -> Option<RelRc<N, E>> {
let weak_ref = self.nodes.get(id).cloned()?;
weak_ref.upgrade()
}
pub fn contains_id(&self, id: NodeId) -> bool {
self.get(id).is_some()
}
pub fn free_node_ids(&mut self) -> usize {
let mut dead_ids = Vec::new();
let mut dead_ptrs = Vec::new();
for (id, weak_ref) in self.nodes.iter() {
if weak_ref.upgrade().is_none() {
dead_ids.push(id);
dead_ptrs.push(weak_ref.as_ptr());
}
}
for id in dead_ids {
self.nodes.remove(id);
}
for ptr in dead_ptrs {
self.ptr_to_id.remove(&ptr);
}
self.nodes.len()
}
pub fn iter(&self) -> impl Iterator<Item = (NodeId, RelRc<N, E>)> + '_ {
self.nodes
.iter()
.filter_map(|(id, weak_ref)| Some((id, weak_ref.upgrade()?)))
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn remove(&mut self, id: NodeId) {
let weak_ref = self.nodes.remove(id);
if let Some(weak_ref) = weak_ref {
self.ptr_to_id.remove(&weak_ref.as_ptr());
}
}
pub(crate) fn as_slotmap(&self) -> &SlotMap<NodeId, RelWeak<N, E>> {
&self.nodes
}
}
impl<'r, N, E> FromIterator<&'r RelRc<N, E>> for Registry<N, E> {
fn from_iter<T: IntoIterator<Item = &'r RelRc<N, E>>>(iter: T) -> Self {
let mut registry = Self::new();
for node in iter {
registry.add_node(node);
}
registry
}
}
impl<N, E> From<Registry<N, E>> for Rc<RefCell<Registry<N, E>>> {
fn from(registry: Registry<N, E>) -> Self {
Rc::new(RefCell::new(registry))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::RelRc;
#[test]
fn test_register_and_get() {
let mut registry = Registry::<&str, ()>::new();
let node = RelRc::new("test");
let id = registry.add_node(&node);
let retrieved = registry.get(id).unwrap();
assert!(RelRc::ptr_eq(&node, &retrieved));
}
#[test]
fn test_duplicate_registration() {
let mut registry = Registry::<&str, ()>::new();
let node = RelRc::new("test");
let id1 = registry.add_node(&node);
let id2 = registry.add_node(&node);
assert_eq!(id1, id2);
}
#[test]
fn test_get_id() {
let mut registry = Registry::<&str, ()>::new();
let node = RelRc::new("test");
assert_eq!(registry.get_id(&node), None);
let id = registry.add_node(&node);
assert_eq!(registry.get_id(&node), Some(id));
}
}