use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// Cluster information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClusterInfo { pub id: String, pub name: String, pub cluster_type: ClusterType, pub url: String, pub port: u16, pub username: String, pub created_at: String, pub updated_at: String, } /// Cluster type #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] #[serde(rename_all = "lowercase")] pub enum ClusterType { #[default] VE, // Proxmox VE PBS, // Proxmox Backup Server } /// Cluster registry for managing multiple clusters pub struct ClusterRegistry { clusters: HashMap, } impl ClusterRegistry { /// Create a new cluster registry pub fn new() -> Self { Self { clusters: HashMap::new(), } } /// Add a cluster pub fn add_cluster(&mut self, cluster: ClusterInfo) { self.clusters.insert(cluster.id.clone(), cluster); } /// Remove a cluster pub fn remove_cluster(&mut self, id: &str) -> Option { self.clusters.remove(id) } /// Get a cluster by ID pub fn get_cluster(&self, id: &str) -> Option<&ClusterInfo> { self.clusters.get(id) } /// Get all clusters pub fn list_clusters(&self) -> Vec<&ClusterInfo> { self.clusters.values().collect() } /// Get clusters by type pub fn list_clusters_by_type(&self, cluster_type: &ClusterType) -> Vec<&ClusterInfo> { self.clusters .values() .filter(|c| &c.cluster_type == cluster_type) .collect() } /// Get cluster count pub fn cluster_count(&self) -> usize { self.clusters.len() } /// Check if a cluster exists pub fn has_cluster(&self, id: &str) -> bool { self.clusters.contains_key(id) } } impl Default for ClusterRegistry { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_cluster_registry_new() { let registry = ClusterRegistry::new(); assert_eq!(registry.cluster_count(), 0); } #[test] fn test_cluster_registry_add_and_get() { let mut registry = ClusterRegistry::new(); let cluster = ClusterInfo { id: "cluster-1".to_string(), name: "Production".to_string(), cluster_type: ClusterType::VE, url: "https://pve.example.com".to_string(), port: 8006, username: "root@pam".to_string(), created_at: "2026-06-10 12:00:00".to_string(), updated_at: "2026-06-10 12:00:00".to_string(), }; registry.add_cluster(cluster.clone()); assert_eq!(registry.cluster_count(), 1); let retrieved = registry.get_cluster("cluster-1"); assert!(retrieved.is_some()); assert_eq!(retrieved.unwrap().name, "Production"); } #[test] fn test_cluster_registry_remove() { let mut registry = ClusterRegistry::new(); let cluster = ClusterInfo { id: "cluster-1".to_string(), name: "Production".to_string(), cluster_type: ClusterType::VE, url: "https://pve.example.com".to_string(), port: 8006, username: "root@pam".to_string(), created_at: "2026-06-10 12:00:00".to_string(), updated_at: "2026-06-10 12:00:00".to_string(), }; registry.add_cluster(cluster); assert_eq!(registry.cluster_count(), 1); let removed = registry.remove_cluster("cluster-1"); assert!(removed.is_some()); assert_eq!(registry.cluster_count(), 0); } #[test] fn test_cluster_registry_list_by_type() { let mut registry = ClusterRegistry::new(); let ve_cluster = ClusterInfo { id: "ve-1".to_string(), name: "VE Cluster".to_string(), cluster_type: ClusterType::VE, url: "https://pve.example.com".to_string(), port: 8006, username: "root@pam".to_string(), created_at: "2026-06-10 12:00:00".to_string(), updated_at: "2026-06-10 12:00:00".to_string(), }; let pbs_cluster = ClusterInfo { id: "pbs-1".to_string(), name: "PBS Cluster".to_string(), cluster_type: ClusterType::PBS, url: "https://pbs.example.com".to_string(), port: 8007, username: "root@pam".to_string(), created_at: "2026-06-10 12:00:00".to_string(), updated_at: "2026-06-10 12:00:00".to_string(), }; registry.add_cluster(ve_cluster); registry.add_cluster(pbs_cluster); let ve_clusters = registry.list_clusters_by_type(&ClusterType::VE); assert_eq!(ve_clusters.len(), 1); let pbs_clusters = registry.list_clusters_by_type(&ClusterType::PBS); assert_eq!(pbs_clusters.len(), 1); } }