pub struct Container {
inner: std::sync::Arc<ContainerInner>,
}
struct ContainerInner {
services: tokio::sync::RwLock<std::collections::HashMap<String, ServiceEntry>>,
}
struct ServiceEntry {
scope: crate::container::scope::Scope,
factory: std::sync::Arc<dyn std::any::Any + Send + Sync>,
singleton_cache: tokio::sync::RwLock<Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>>,
}
impl Container {
pub fn new() -> Self {
Self {
inner: std::sync::Arc::new(ContainerInner {
services: tokio::sync::RwLock::const_new(std::collections::HashMap::new()),
}),
}
}
pub async fn register<T: 'static + Send + Sync>(
&self,
name: impl Into<String>,
provider: impl crate::container::provider::Provider<T> + 'static,
scope: crate::container::scope::Scope,
) -> crate::result::hex_result::HexResult<()> {
let name = name.into();
let mut services = self.inner.services.write().await;
if services.contains_key(&name) {
return Err(
crate::error::hex_error::Hexserror::validation(&format!(
"Service {} already registered",
name
))
.with_next_step("Use different service name or remove existing registration"),
);
}
let boxed_provider: Box<dyn crate::container::provider::Provider<T>> = Box::new(provider);
services.insert(
name,
ServiceEntry {
scope,
factory: std::sync::Arc::new(boxed_provider),
singleton_cache: tokio::sync::RwLock::const_new(None),
},
);
Ok(())
}
pub async fn resolve<T: 'static + Send + Sync>(
&self,
name: &str,
) -> crate::result::hex_result::HexResult<std::sync::Arc<T>> {
let services = self.inner.services.read().await;
let entry = services
.get(name)
.ok_or_else(|| crate::error::hex_error::Hexserror::not_found("Service", name))?;
match entry.scope {
crate::container::scope::Scope::Singleton => {
let mut cache = entry.singleton_cache.write().await;
if let Some(cached) = cache.as_ref() {
return cached.clone().downcast::<T>().map_err(|_| {
crate::error::hex_error::Hexserror::adapter("E_CNT_004", "Type mismatch")
});
}
let provider = entry
.factory
.downcast_ref::<Box<dyn crate::container::provider::Provider<T>>>()
.ok_or_else(|| {
crate::error::hex_error::Hexserror::adapter("E_CNT_005", "Provider type mismatch")
})?;
let instance = provider.provide()?;
let arc_instance = std::sync::Arc::new(instance);
*cache = Some(arc_instance.clone() as std::sync::Arc<dyn std::any::Any + Send + Sync>);
Ok(arc_instance)
}
crate::container::scope::Scope::Transient => {
let provider = entry
.factory
.downcast_ref::<Box<dyn crate::container::provider::Provider<T>>>()
.ok_or_else(|| {
crate::error::hex_error::Hexserror::adapter("E_CNT_006", "Provider type mismatch")
})?;
let instance = provider.provide()?;
Ok(std::sync::Arc::new(instance))
}
}
}
pub async fn contains(&self, name: &str) -> bool {
self.inner.services.read().await.contains_key(name)
}
pub async fn service_count(&self) -> usize {
self.inner.services.read().await.len()
}
#[cfg(feature = "container")]
pub async fn register_async<T: 'static + Send + Sync>(
&self,
name: impl Into<String>,
provider: impl crate::container::async_provider::AsyncProvider<T> + 'static,
scope: crate::container::scope::Scope,
) -> crate::result::hex_result::HexResult<()> {
let name = name.into();
let mut services = self.inner.services.write().await;
if services.contains_key(&name) {
return Err(
crate::error::hex_error::Hexserror::validation(&format!(
"Service {} already registered",
name
))
.with_next_step("Use different service name or remove existing registration"),
);
}
let boxed_provider: Box<dyn crate::container::async_provider::AsyncProvider<T>> =
Box::new(provider);
services.insert(
name,
ServiceEntry {
scope,
factory: std::sync::Arc::new(boxed_provider),
singleton_cache: tokio::sync::RwLock::const_new(None),
},
);
Ok(())
}
#[cfg(feature = "container")]
pub async fn resolve_async<T: 'static + Send + Sync>(
&self,
name: &str,
) -> crate::result::hex_result::HexResult<std::sync::Arc<T>> {
let services = self.inner.services.read().await;
let entry = services
.get(name)
.ok_or_else(|| crate::error::hex_error::Hexserror::not_found("Service", name))?;
match entry.scope {
crate::container::scope::Scope::Singleton => {
let mut cache = entry.singleton_cache.write().await;
if let Some(cached) = cache.as_ref() {
return cached.clone().downcast::<T>().map_err(|_| {
crate::error::hex_error::Hexserror::adapter("E_CNT_004", "Type mismatch")
});
}
let provider = entry
.factory
.downcast_ref::<Box<dyn crate::container::async_provider::AsyncProvider<T>>>()
.ok_or_else(|| {
crate::error::hex_error::Hexserror::adapter("E_CNT_007", "Async provider type mismatch")
})?;
let instance = provider.provide_async().await?;
let arc_instance = std::sync::Arc::new(instance);
*cache = Some(arc_instance.clone() as std::sync::Arc<dyn std::any::Any + Send + Sync>);
Ok(arc_instance)
}
crate::container::scope::Scope::Transient => {
let provider = entry
.factory
.downcast_ref::<Box<dyn crate::container::async_provider::AsyncProvider<T>>>()
.ok_or_else(|| {
crate::error::hex_error::Hexserror::adapter("E_CNT_008", "Async provider type mismatch")
})?;
let instance = provider.provide_async().await?;
Ok(std::sync::Arc::new(instance))
}
}
}
}
impl Clone for Container {
fn clone(&self) -> Self {
Self {
inner: std::sync::Arc::clone(&self.inner),
}
}
}
impl Default for Container {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestService {
value: i32,
}
struct TestProvider {
value: i32,
}
impl crate::container::provider::Provider<TestService> for TestProvider {
fn provide(&self) -> crate::result::hex_result::HexResult<TestService> {
Ok(TestService { value: self.value })
}
}
#[tokio::test]
async fn test_container_new() {
let container = Container::new();
assert_eq!(container.service_count().await, 0);
}
#[tokio::test]
async fn test_container_register() {
let container = Container::new();
let provider = TestProvider { value: 42 };
container
.register(
"test_service",
provider,
crate::container::scope::Scope::Singleton,
)
.await
.unwrap();
assert_eq!(container.service_count().await, 1);
assert!(container.contains("test_service").await);
}
#[tokio::test]
async fn test_container_duplicate_registration_fails() {
let container = Container::new();
let provider1 = TestProvider { value: 1 };
let provider2 = TestProvider { value: 2 };
container
.register("test", provider1, crate::container::scope::Scope::Singleton)
.await
.unwrap();
let result = container
.register("test", provider2, crate::container::scope::Scope::Singleton)
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_container_clone_shares_services() {
let container1 = Container::new();
let provider = TestProvider { value: 10 };
container1
.register(
"shared",
provider,
crate::container::scope::Scope::Singleton,
)
.await
.unwrap();
let container2 = container1.clone();
assert!(container2.contains("shared").await);
assert_eq!(container2.service_count().await, 1);
}
}