use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
pub(crate) mod bootstrap;
#[derive(Error, Debug, Clone)]
pub enum RegistryError {
#[error("服务 '{name}' 已存在")]
ServiceAlreadyExists {
name: String,
},
#[error("服务 '{name}' 不存在")]
ServiceNotFound {
name: String,
},
}
pub type RegistryResult<T> = Result<T, RegistryError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceMetadata {
pub name: String,
pub version: String,
pub description: Option<String>,
pub dependencies: Vec<String>,
pub provides: Vec<String>,
pub priority: u32,
}
#[derive(Debug, Clone)]
pub struct ServiceEntry {
pub metadata: ServiceMetadata,
}
pub trait ServiceRegistry: Send + Sync {
fn get_service(&self, name: &str) -> RegistryResult<&ServiceEntry>;
fn list_services(&self) -> Vec<&ServiceEntry>;
fn has_service(&self, name: &str) -> bool;
fn get_dependency_graph(&self) -> HashMap<String, Vec<String>>;
}
#[derive(Debug)]
pub struct DefaultServiceRegistry {
services: HashMap<String, ServiceEntry>,
}
impl Default for DefaultServiceRegistry {
fn default() -> Self {
Self::new()
}
}
impl DefaultServiceRegistry {
pub fn new() -> Self {
Self {
services: HashMap::new(),
}
}
pub(crate) fn register_service(&mut self, metadata: ServiceMetadata) -> RegistryResult<()> {
if self.services.contains_key(&metadata.name) {
return Err(RegistryError::ServiceAlreadyExists {
name: metadata.name,
});
}
let name = metadata.name.clone();
self.services.insert(name, ServiceEntry { metadata });
Ok(())
}
}
impl ServiceRegistry for DefaultServiceRegistry {
fn get_service(&self, name: &str) -> RegistryResult<&ServiceEntry> {
self.services
.get(name)
.ok_or_else(|| RegistryError::ServiceNotFound {
name: name.to_string(),
})
}
fn list_services(&self) -> Vec<&ServiceEntry> {
let mut entries: Vec<&ServiceEntry> = self.services.values().collect();
entries.sort_by(|a, b| {
a.metadata
.priority
.cmp(&b.metadata.priority)
.then_with(|| a.metadata.name.cmp(&b.metadata.name))
});
entries
}
fn has_service(&self, name: &str) -> bool {
self.services.contains_key(name)
}
fn get_dependency_graph(&self) -> HashMap<String, Vec<String>> {
self.services
.iter()
.map(|(name, entry)| (name.clone(), entry.metadata.dependencies.clone()))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_metadata(name: &str) -> ServiceMetadata {
ServiceMetadata {
name: name.to_string(),
version: "1.0.0".to_string(),
description: Some("测试服务".to_string()),
dependencies: vec![],
provides: vec!["test-feature".to_string()],
priority: 1,
}
}
#[test]
fn test_service_registration_and_lookup() {
let mut registry = DefaultServiceRegistry::new();
registry
.register_service(sample_metadata("test-service"))
.unwrap();
assert!(registry.has_service("test-service"));
let entry = registry.get_service("test-service").unwrap();
assert_eq!(entry.metadata.name, "test-service");
assert_eq!(entry.metadata.priority, 1);
assert_eq!(registry.list_services().len(), 1);
}
#[test]
fn test_duplicate_registration() {
let mut registry = DefaultServiceRegistry::new();
registry
.register_service(sample_metadata("test-service"))
.unwrap();
let result = registry.register_service(sample_metadata("test-service"));
assert!(matches!(
result,
Err(RegistryError::ServiceAlreadyExists { .. })
));
}
#[test]
fn test_missing_service() {
let registry = DefaultServiceRegistry::new();
assert!(!registry.has_service("missing"));
assert!(matches!(
registry.get_service("missing"),
Err(RegistryError::ServiceNotFound { .. })
));
}
#[test]
fn test_dependency_graph() {
let mut registry = DefaultServiceRegistry::new();
registry
.register_service(ServiceMetadata {
name: "comm".to_string(),
version: "1.0.0".to_string(),
description: None,
dependencies: vec!["auth".to_string()],
provides: vec!["im".to_string()],
priority: 2,
})
.unwrap();
let graph = registry.get_dependency_graph();
assert_eq!(graph.get("comm").unwrap(), &vec!["auth".to_string()]);
}
#[test]
fn list_services_stable_order_by_priority_then_name() {
let mut registry = DefaultServiceRegistry::new();
for (name, priority) in [("zeta", 2u32), ("alpha", 1), ("beta", 1), ("mid", 2)] {
registry
.register_service(ServiceMetadata {
name: name.to_string(),
version: "1.0.0".to_string(),
description: None,
dependencies: vec![],
provides: vec![],
priority,
})
.unwrap();
}
let names: Vec<&str> = registry
.list_services()
.into_iter()
.map(|e| e.metadata.name.as_str())
.collect();
assert_eq!(names, vec!["alpha", "beta", "mid", "zeta"]);
}
}