use std::sync::{OnceLock, Weak};
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::traits::DepNode;
const MAX_VALUE_LEN: usize = 4096;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CellSnapshot {
pub id: Uuid,
pub name: Option<String>,
pub display_name: String,
pub subscriber_count: usize,
pub owned_count: usize,
pub dep_ids: Vec<Uuid>,
pub owner_id: Option<Uuid>,
#[serde(default)]
pub value: Option<String>,
#[serde(default)]
pub caller: Option<String>,
}
pub struct CellRegistry {
cells: DashMap<Uuid, Weak<dyn DepNode>>,
ownership: DashMap<Uuid, Uuid>,
}
impl CellRegistry {
fn new() -> Self {
Self {
cells: DashMap::new(),
ownership: DashMap::new(),
}
}
pub fn register(&self, id: Uuid, weak: Weak<dyn DepNode>) {
self.cells.insert(id, weak);
}
pub fn deregister(&self, id: &Uuid) {
self.cells.remove(id);
self.ownership.remove(id);
}
pub fn mark_owned(&self, child_id: Uuid, parent_id: Uuid) {
self.ownership.insert(child_id, parent_id);
}
pub fn unmark_owned(&self, child_id: Uuid) {
self.ownership.remove(&child_id);
}
pub fn snapshot(&self) -> Vec<CellSnapshot> {
let mut snapshots = Vec::new();
let mut stale = Vec::new();
for entry in self.cells.iter() {
let id = *entry.key();
match entry.value().upgrade() {
Some(node) => {
let dep_ids: Vec<Uuid> = node.deps().iter().map(|d| d.id()).collect();
let owner_id = self.ownership.get(&id).map(|e| *e.value());
let value = node.value_debug().map(|mut s| {
if s.len() > MAX_VALUE_LEN {
s.truncate(MAX_VALUE_LEN);
s.push('…');
}
s
});
let caller = node
.caller()
.map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column()));
snapshots.push(CellSnapshot {
id,
name: node.name(),
display_name: node.display_name(),
subscriber_count: node.subscriber_count(),
owned_count: node.owned_count(),
dep_ids,
owner_id,
value,
caller,
});
}
None => {
stale.push(id);
}
}
}
for id in stale {
self.cells.remove(&id);
self.ownership.remove(&id);
}
snapshots
}
pub fn gc(&self) {
let stale: Vec<Uuid> = self
.cells
.iter()
.filter(|e| e.value().upgrade().is_none())
.map(|e| *e.key())
.collect();
for id in stale {
self.cells.remove(&id);
self.ownership.remove(&id);
}
}
pub fn len(&self) -> usize {
self.cells.len()
}
pub fn is_empty(&self) -> bool {
self.cells.is_empty()
}
}
static REGISTRY: OnceLock<CellRegistry> = OnceLock::new();
pub fn registry() -> &'static CellRegistry {
REGISTRY.get_or_init(CellRegistry::new)
}