use anyhow::{Context, Result};
use dashmap::DashMap;
use std::collections::HashMap;
use tokio::sync::RwLock;
use tracing::debug;
use crate::domain::capability::{Capability, CapabilityPath};
pub struct CapabilityRegistry {
capabilities: DashMap<String, Capability>,
capability_root: RwLock<CapabilityNode>,
}
impl CapabilityRegistry {
pub fn new() -> Self {
Self {
capabilities: DashMap::new(),
capability_root: RwLock::new(CapabilityNode::new("root")),
}
}
pub async fn register(&self, capability: Capability) -> Result<()> {
let cap_id = capability.id.clone();
let capability_path = capability.capability_path.clone();
if self.capabilities.contains_key(&cap_id) {
return Err(anyhow::anyhow!("Capability already registered: {}", cap_id));
}
self.capabilities.insert(cap_id.clone(), capability);
let mut root = self.capability_root.write().await;
root.add_capability(&capability_path, &cap_id);
debug!(
"Registered capability: {} in path: {}",
cap_id,
capability_path.to_path_string()
);
Ok(())
}
pub fn get(&self, id: &str) -> Option<Capability> {
self.capabilities.get(id).map(|c| c.clone())
}
pub fn contains(&self, id: &str) -> bool {
self.capabilities.contains_key(id)
}
pub async fn find_by_path(&self, path: &str) -> Vec<Capability> {
let path = CapabilityPath::from_string(path);
let root = self.capability_root.read().await;
let mut node = &*root;
for segment in path.segments() {
match node.children.get(segment) {
Some(child) => node = child,
None => return Vec::new(),
}
}
let mut capability_ids = Vec::new();
Self::collect_capability_ids(node, &mut capability_ids);
capability_ids
.iter()
.filter_map(|id| self.capabilities.get(id).map(|c| c.clone()))
.collect()
}
fn collect_capability_ids(node: &CapabilityNode, result: &mut Vec<String>) {
result.extend(node.capabilities.iter().cloned());
for child in node.children.values() {
Self::collect_capability_ids(child, result);
}
}
pub async fn find_direct_by_path(&self, path: &str) -> Vec<Capability> {
let path = CapabilityPath::from_string(path);
let root = self.capability_root.read().await;
let mut node = &*root;
for segment in path.segments() {
match node.children.get(segment) {
Some(child) => node = child,
None => return Vec::new(),
}
}
node.capabilities
.iter()
.filter_map(|id| self.capabilities.get(id).map(|c| c.clone()))
.collect()
}
pub async fn list_sub_paths(&self, path: &str) -> Vec<String> {
let path = CapabilityPath::from_string(path);
let root = self.capability_root.read().await;
let mut node = &*root;
for segment in path.segments() {
match node.children.get(segment) {
Some(child) => node = child,
None => return Vec::new(),
}
}
node.children.keys().cloned().collect()
}
pub fn get_capability_path(&self, capability_id: &str) -> Option<CapabilityPath> {
self.capabilities
.get(capability_id)
.map(|c| c.capability_path.clone())
}
pub async fn get_capability_tree(&self) -> CapabilityNode {
let root = self.capability_root.read().await;
root.clone()
}
pub fn list_all(&self) -> Vec<Capability> {
self.capabilities.iter().map(|c| c.clone()).collect()
}
pub async fn list_all_paths(&self) -> Vec<String> {
let root = self.capability_root.read().await;
let mut paths = Vec::new();
Self::collect_capability_paths(&root, String::new(), &mut paths);
paths
}
fn collect_capability_paths(node: &CapabilityNode, prefix: String, result: &mut Vec<String>) {
for (name, child) in &node.children {
let path = if prefix.is_empty() {
name.clone()
} else {
format!("{}/{}", prefix, name)
};
result.push(path.clone());
Self::collect_capability_paths(child, path, result);
}
}
pub async fn unregister(&self, capability_id: &str) -> Result<()> {
let capability = self
.capabilities
.remove(capability_id)
.map(|(_, c)| c)
.context("Capability not found")?;
let mut root = self.capability_root.write().await;
root.remove_capability(&capability.capability_path, capability_id);
debug!("Unregistered capability: {}", capability_id);
Ok(())
}
pub fn len(&self) -> usize {
self.capabilities.len()
}
pub fn is_empty(&self) -> bool {
self.capabilities.is_empty()
}
}
impl Default for CapabilityRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct CapabilityNode {
pub name: String,
capabilities: Vec<String>,
children: HashMap<String, CapabilityNode>,
}
impl CapabilityNode {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
capabilities: Vec::new(),
children: HashMap::new(),
}
}
pub fn add_capability(&mut self, path: &CapabilityPath, capability_id: &str) {
let mut node = self;
for segment in path.segments() {
node = node
.children
.entry(segment.to_string())
.or_insert_with(|| CapabilityNode::new(segment));
}
node.capabilities.push(capability_id.to_string());
}
pub fn remove_capability(&mut self, path: &CapabilityPath, capability_id: &str) {
let mut node = self;
for segment in path.segments() {
match node.children.get_mut(segment) {
Some(child) => node = child,
None => return,
}
}
node.capabilities.retain(|id| id != capability_id);
}
pub fn children(&self) -> &HashMap<String, CapabilityNode> {
&self.children
}
pub fn capabilities(&self) -> &[String] {
&self.capabilities
}
}