use anyhow::Result;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
pub trait Service: Send + Sync {
fn service_name(&self) -> &'static str;
fn initialize(&mut self) -> Result<()> {
Ok(())
}
fn health_check(&self) -> Result<()> {
Ok(())
}
fn shutdown(&mut self) -> Result<()> {
Ok(())
}
}
#[async_trait::async_trait]
pub trait AnalysisService: Service {
type Input;
type Output;
type Error: std::error::Error + Send + Sync;
async fn analyze(&self, input: Self::Input) -> Result<Self::Output, Self::Error>;
fn capabilities(&self) -> AnalysisCapabilities {
AnalysisCapabilities::default()
}
}
#[derive(Debug, Clone)]
pub struct AnalysisCapabilities {
pub supports_batch: bool,
pub supports_streaming: bool,
pub max_file_size: Option<usize>,
pub supported_languages: Vec<String>,
}
impl Default for AnalysisCapabilities {
fn default() -> Self {
Self {
supports_batch: true,
supports_streaming: false,
max_file_size: None,
supported_languages: vec!["rust".to_string()],
}
}
}
pub struct ServiceRegistry {
services: Arc<RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>>,
service_names: Arc<RwLock<HashMap<TypeId, &'static str>>>,
}
impl ServiceRegistry {
#[must_use]
pub fn new() -> Self {
Self {
services: Arc::new(RwLock::new(HashMap::new())),
service_names: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn register<T: Service + 'static>(&self, mut service: T) -> Result<()> {
service.initialize()?;
let service_name = service.service_name();
let type_id = TypeId::of::<T>();
{
let mut services = self.services.write().unwrap();
services.insert(type_id, Box::new(Arc::new(service)));
}
{
let mut names = self.service_names.write().unwrap();
names.insert(type_id, service_name);
}
Ok(())
}
pub fn get<T: Service + 'static>(&self) -> Result<Arc<T>> {
let type_id = TypeId::of::<T>();
let services = self.services.read().unwrap();
let service = services
.get(&type_id)
.ok_or_else(|| anyhow::anyhow!("Service not found: {}", std::any::type_name::<T>()))?;
let service = service.downcast_ref::<Arc<T>>().ok_or_else(|| {
anyhow::anyhow!("Service type mismatch for: {}", std::any::type_name::<T>())
})?;
Ok(Arc::clone(service))
}
#[must_use]
pub fn has<T: Service + 'static>(&self) -> bool {
let type_id = TypeId::of::<T>();
let services = self.services.read().unwrap();
services.contains_key(&type_id)
}
#[must_use]
pub fn list_services(&self) -> Vec<&'static str> {
let names = self.service_names.read().unwrap();
names.values().copied().collect()
}
pub fn health_check_all(&self) -> Result<Vec<(&'static str, Result<()>)>> {
Ok(vec![])
}
pub fn shutdown_all(&mut self) -> Result<()> {
Ok(())
}
}
impl Default for ServiceRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct ServiceRegistryBuilder {
registry: ServiceRegistry,
}
impl ServiceRegistryBuilder {
#[must_use]
pub fn new() -> Self {
Self {
registry: ServiceRegistry::new(),
}
}
pub fn with_service<T: Service + 'static>(self, service: T) -> Result<Self> {
self.registry.register(service)?;
Ok(self)
}
#[must_use]
pub fn build(self) -> ServiceRegistry {
self.registry
}
}
impl Default for ServiceRegistryBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestService {
name: &'static str,
}
impl Service for TestService {
fn service_name(&self) -> &'static str {
self.name
}
}
#[test]
fn test_service_registry_creation() {
let registry = ServiceRegistry::new();
assert_eq!(registry.list_services().len(), 0);
}
#[test]
fn test_service_registration() -> Result<()> {
let registry = ServiceRegistry::new();
let service = TestService {
name: "test_service",
};
registry.register(service)?;
assert!(registry.has::<TestService>());
assert_eq!(registry.list_services(), vec!["test_service"]);
Ok(())
}
#[test]
fn test_service_retrieval() -> Result<()> {
let registry = ServiceRegistry::new();
let service = TestService {
name: "test_service",
};
registry.register(service)?;
let retrieved = registry.get::<TestService>()?;
assert_eq!(retrieved.service_name(), "test_service");
Ok(())
}
#[test]
fn test_service_builder() -> Result<()> {
let registry = ServiceRegistryBuilder::new()
.with_service(TestService { name: "service1" })?
.with_service(TestService { name: "service2" })?
.build();
assert_eq!(registry.list_services().len(), 2);
Ok(())
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}