revoke-registry 0.3.0

Service registry and discovery for Revoke microservices framework
Documentation
use async_trait::async_trait;
use revoke_core::{HealthStatus, Result, ServiceInfo, ServiceRegistry, Status};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::info;
use uuid::Uuid;

pub struct MemoryRegistry {
    services: Arc<RwLock<HashMap<String, Vec<ServiceInfo>>>>,
    health_status: Arc<RwLock<HashMap<Uuid, HealthStatus>>>,
}

impl MemoryRegistry {
    pub fn new() -> Self {
        Self {
            services: Arc::new(RwLock::new(HashMap::new())),
            health_status: Arc::new(RwLock::new(HashMap::new())),
        }
    }
}

impl Default for MemoryRegistry {
    fn default() -> Self {
        Self::new()
    }
}

#[async_trait]
impl ServiceRegistry for MemoryRegistry {
    async fn register(&self, service: ServiceInfo) -> Result<()> {
        let mut services = self.services.write().await;
        services
            .entry(service.name.clone())
            .or_insert_with(Vec::new)
            .push(service.clone());
        
        info!("Service {} registered with ID {}", service.name, service.id);
        Ok(())
    }

    async fn deregister(&self, service_id: Uuid) -> Result<()> {
        let mut services = self.services.write().await;
        
        for (_, service_list) in services.iter_mut() {
            service_list.retain(|s| s.id != service_id);
        }
        
        let mut health = self.health_status.write().await;
        health.remove(&service_id);
        
        info!("Service {} deregistered", service_id);
        Ok(())
    }

    async fn get_service(&self, name: &str) -> Result<Vec<ServiceInfo>> {
        let services = self.services.read().await;
        let health = self.health_status.read().await;
        
        Ok(services
            .get(name)
            .map(|list| {
                list.iter()
                    .filter(|s| {
                        health
                            .get(&s.id)
                            .map(|h| h.status == Status::Healthy)
                            .unwrap_or(true)
                    })
                    .cloned()
                    .collect()
            })
            .unwrap_or_default())
    }

    async fn update_health(&self, status: HealthStatus) -> Result<()> {
        let mut health = self.health_status.write().await;
        health.insert(status.service_id, status);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use revoke_core::Protocol;

    #[tokio::test]
    async fn test_memory_registry() {
        let registry = MemoryRegistry::new();
        
        let service = ServiceInfo {
            id: Uuid::new_v4(),
            name: "test-service".to_string(),
            version: "1.0.0".to_string(),
            address: "127.0.0.1".to_string(),
            port: 8080,
            protocol: Protocol::Http,
            metadata: HashMap::new(),
        };
        
        registry.register(service.clone()).await.unwrap();
        
        let services = registry.get_service("test-service").await.unwrap();
        assert_eq!(services.len(), 1);
        assert_eq!(services[0].id, service.id);
        
        registry.deregister(service.id).await.unwrap();
        
        let services = registry.get_service("test-service").await.unwrap();
        assert_eq!(services.len(), 0);
    }
}