use crate::Container;
use crate::error::Error;
pub trait Inject: Sized + Send + Sync {
fn inject(container: &Container) -> Result<Self, Error>;
}
impl Inject for Container {
#[inline]
fn inject(container: &Container) -> Result<Self, Error> {
Ok(container.clone())
}
}
impl Inject for () {
#[inline]
fn inject(_: &Container) -> Result<Self, Error> {
Ok(())
}
}
macro_rules! define_inject {
($($T: ident),*) => {
impl<$($T: Inject),+> Inject for ($($T,)+) {
#[inline]
#[allow(non_snake_case)]
fn inject(container: &Container) -> Result<Self, Error> {
let tuple = (
$(
$T::inject(container)?,
)*
);
Ok(tuple)
}
}
}
}
define_inject! { T1 }
define_inject! { T1, T2 }
define_inject! { T1, T2, T3 }
define_inject! { T1, T2, T3, T4 }
define_inject! { T1, T2, T3, T4, T5 }
#[cfg(test)]
mod tests {
use super::*;
use crate::container::ContainerBuilder;
use std::sync::{Arc, Mutex};
#[derive(Default, Clone)]
struct SimpleService {
value: i32,
}
impl Inject for SimpleService {
fn inject(_: &Container) -> Result<Self, Error> {
Ok(Default::default())
}
}
#[derive(Clone)]
struct ServiceWithDependency {
service: SimpleService,
multiplier: i32,
}
impl Inject for ServiceWithDependency {
fn inject(container: &Container) -> Result<Self, Error> {
let service = container.resolve::<SimpleService>()?;
Ok(Self {
service,
multiplier: 2,
})
}
}
#[derive(Clone)]
struct ComplexService {
dependency: ServiceWithDependency,
data: Arc<Mutex<Vec<String>>>,
}
impl Inject for ComplexService {
fn inject(container: &Container) -> Result<Self, Error> {
let dependency = container.resolve::<ServiceWithDependency>()?;
Ok(Self {
dependency,
data: Arc::new(Mutex::new(vec!["test".to_string()])),
})
}
}
#[derive(Debug)]
struct FailingService;
impl Inject for FailingService {
fn inject(_: &Container) -> Result<Self, Error> {
Err(Error::Other("Injection failed"))
}
}
#[test]
fn it_injects_default_service() {
let container = ContainerBuilder::new().build();
let result = SimpleService::inject(&container);
assert!(result.is_ok());
let service = result.unwrap();
assert_eq!(service.value, 0);
}
#[test]
#[allow(clippy::redundant_closure)]
fn it_injects_service_with_dependencies() {
let mut builder = ContainerBuilder::new();
builder.register_scoped_factory(|c: Container| SimpleService::inject(&c));
let container = builder.build().create_scope();
let result = ServiceWithDependency::inject(&container);
assert!(result.is_ok());
let service = result.unwrap();
assert_eq!(service.service.value, 0);
assert_eq!(service.multiplier, 2);
}
#[test]
fn it_injects_complex_service_with_nested_dependencies() {
let mut builder = ContainerBuilder::new();
builder.register_scoped::<SimpleService>();
builder.register_scoped::<ServiceWithDependency>();
let container = builder.build().create_scope();
let result = ComplexService::inject(&container);
assert!(result.is_ok());
let service = result.unwrap();
assert_eq!(service.dependency.service.value, 0);
assert_eq!(service.dependency.multiplier, 2);
let data = service.data.lock().unwrap();
assert_eq!(data[0], "test");
}
#[test]
fn it_fails_when_dependency_not_registered() {
let container = ContainerBuilder::new().build();
let result = ServiceWithDependency::inject(&container);
assert!(result.is_err());
}
#[test]
fn it_handles_injection_errors() {
let container = ContainerBuilder::new().build();
let result = FailingService::inject(&container);
assert!(result.is_err());
match result.unwrap_err() {
Error::Other(msg) => assert_eq!(msg, "Injection failed"),
_ => panic!("Expected Other error"),
}
}
#[test]
fn it_uses_default_trait_implementation_for_inject() {
let container = ContainerBuilder::new().build();
let result = <SimpleService as Inject>::inject(&container);
assert!(result.is_ok());
let service = result.unwrap();
assert_eq!(service.value, 0);
}
#[test]
fn it_resolves_same_dependency_multiple_times() {
let mut builder = ContainerBuilder::new();
builder.register_scoped::<SimpleService>();
let container = builder.build().create_scope();
let result1 = ServiceWithDependency::inject(&container);
let result2 = ServiceWithDependency::inject(&container);
assert!(result1.is_ok());
assert!(result2.is_ok());
let service1 = result1.unwrap();
let service2 = result2.unwrap();
assert_eq!(service1.service.value, service2.service.value);
assert_eq!(service1.multiplier, service2.multiplier);
}
#[test]
fn it_works_with_different_service_lifetimes() {
let mut builder = ContainerBuilder::new();
builder.register_singleton(SimpleService { value: 100 });
builder.register_transient::<ServiceWithDependency>();
let container = builder.build();
let result1 = ServiceWithDependency::inject(&container);
let result2 = ServiceWithDependency::inject(&container);
assert!(result1.is_ok());
assert!(result2.is_ok());
let service1 = result1.unwrap();
let service2 = result2.unwrap();
assert_eq!(service1.service.value, 100);
assert_eq!(service2.service.value, 100);
}
#[test]
fn it_tests_send_sync_requirements() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<SimpleService>();
assert_send_sync::<ServiceWithDependency>();
assert_send_sync::<ComplexService>();
}
}