//! Hand and Trigger registries use std::collections::HashMap; use std::sync::Arc; use tokio::sync::{RwLock, Semaphore}; use zclaw_types::Result; use super::{Hand, HandConfig, HandContext, HandResult}; /// Hand registry with per-hand concurrency control (P2-01) pub struct HandRegistry { hands: RwLock>>, configs: RwLock>, /// Per-hand semaphores for max_concurrent enforcement (key: hand id) semaphores: RwLock>>, } impl HandRegistry { pub fn new() -> Self { Self { hands: RwLock::new(HashMap::new()), configs: RwLock::new(HashMap::new()), semaphores: RwLock::new(HashMap::new()), } } /// Register a hand pub async fn register(&self, hand: Arc) { let config = hand.config().clone(); let mut hands = self.hands.write().await; let mut configs = self.configs.write().await; // P2-01: Create semaphore for max_concurrent enforcement if config.max_concurrent > 0 { let mut semaphores = self.semaphores.write().await; semaphores.insert( config.id.clone(), Arc::new(Semaphore::new(config.max_concurrent as usize)), ); } hands.insert(config.id.clone(), hand); configs.insert(config.id.clone(), config); } /// Get a hand by ID pub async fn get(&self, id: &str) -> Option> { let hands = self.hands.read().await; hands.get(id).cloned() } /// Get hand configuration pub async fn get_config(&self, id: &str) -> Option { let configs = self.configs.read().await; configs.get(id).cloned() } /// List all hands pub async fn list(&self) -> Vec { let configs = self.configs.read().await; configs.values().cloned().collect() } /// Execute a hand with concurrency limiting (P2-01) pub async fn execute( &self, id: &str, context: &HandContext, input: serde_json::Value, ) -> Result { let hand = self.get(id).await .ok_or_else(|| zclaw_types::ZclawError::NotFound(format!("Hand not found: {}", id)))?; // P2-01: Acquire semaphore permit if max_concurrent is set let semaphore_opt = { let semaphores = self.semaphores.read().await; semaphores.get(id).cloned() }; if let Some(semaphore) = semaphore_opt { let _permit = semaphore.acquire().await .map_err(|_| zclaw_types::ZclawError::Internal( format!("Hand '{}' semaphore closed", id) ))?; hand.execute(context, input).await } else { hand.execute(context, input).await } } /// Remove a hand pub async fn remove(&self, id: &str) { let mut hands = self.hands.write().await; let mut configs = self.configs.write().await; let mut semaphores = self.semaphores.write().await; hands.remove(id); configs.remove(id); semaphores.remove(id); } /// P2-03: Get tool and metric counts for a hand pub async fn get_counts(&self, id: &str) -> (u32, u32) { let hands = self.hands.read().await; if let Some(hand) = hands.get(id) { (hand.tool_count(), hand.metric_count()) } else { (0, 0) } } }