rust-dix 0.6.0

rust-dix: A Rust dependency injection framework inspired by Microsoft.Extensions.DependencyInjection
Documentation
mod common;

use rust_dix::*;
use std::sync::Arc;

#[test]
fn get_all_returns_all_entries() {
    let p = ServiceCollection::new()
        .singleton(|_| Arc::new(common::MyService { value: 1 }))
        .keyed_singleton("extra", |_| Arc::new(common::MyService { value: 2 }))
        .build()
        .unwrap();
    let all: Vec<Arc<common::MyService>> = p.get_all();
    assert_eq!(all.len(), 2);
}

#[test]
fn scope_get_keyed_found() {
    let p = ServiceCollection::new()
        .keyed_scoped("k", |_| Arc::new(common::MyService { value: 10 }))
        .build()
        .unwrap();
    let scope = p.scope();
    let svc: Arc<common::MyService> = scope.get_keyed("k").unwrap();
    assert_eq!(svc.value, 10);
}

#[test]
fn scope_get_none_on_missing() {
    let p = ServiceCollection::new().build().unwrap();
    let scope = p.scope();
    assert!(scope.get_optional::<common::MyService>().is_none());
}

#[test]
fn scope_get_all_from_parent() {
    let p = ServiceCollection::new()
        .keyed_singleton("a", |_| Arc::new(common::MyService { value: 1 }))
        .keyed_singleton("b", |_| Arc::new(common::MyService { value: 2 }))
        .build()
        .unwrap();
    let scope = p.scope();
    let all: Vec<Arc<common::MyService>> = scope.get_all();
    assert_eq!(all.len(), 2);
}

#[test]
fn instance_preserves_arc() {
    let svc = Arc::new(common::MyService { value: 77 });
    let p = ServiceCollection::new()
        .instance(svc.clone())
        .build()
        .unwrap();
    let retrieved: Arc<common::MyService> = p.get().unwrap();
    assert!(Arc::ptr_eq(&svc, &retrieved));
}

#[test]
fn singleton_caches() {
    use std::sync::atomic::{AtomicUsize, Ordering};
    static COUNT: AtomicUsize = AtomicUsize::new(0);
    let p = ServiceCollection::new()
        .singleton(|_| {
            COUNT.fetch_add(1, Ordering::SeqCst);
            Arc::new(common::MyService { value: 1 })
        })
        .build()
        .unwrap();
    let _a: Arc<common::MyService> = p.get().unwrap();
    let _b: Arc<common::MyService> = p.get().unwrap();
    assert_eq!(COUNT.load(Ordering::SeqCst), 1);
}

#[test]
fn transient_creates_new_each_time() {
    use std::sync::atomic::{AtomicUsize, Ordering};
    static COUNT: AtomicUsize = AtomicUsize::new(0);
    let p = ServiceCollection::new()
        .transient(|_| {
            COUNT.fetch_add(1, Ordering::SeqCst);
            Arc::new(common::MyService { value: 1 })
        })
        .build()
        .unwrap();
    let _a: Arc<common::MyService> = p.get().unwrap();
    let _b: Arc<common::MyService> = p.get().unwrap();
    assert_eq!(COUNT.load(Ordering::SeqCst), 2);
}

#[test]
fn try_add_skips_existing() {
    let p = ServiceCollection::new()
        .singleton(|_| Arc::new(common::MyService { value: 1 }))
        .try_add(|_| Arc::new(common::MyService { value: 2 }))
        .build()
        .unwrap();
    assert_eq!(p.get::<common::MyService>().unwrap().value, 1);
}

#[test]
fn wrapper_get_optional_child_first() {
    let root = ServiceCollection::new()
        .singleton(|_| Arc::new(common::MyService { value: 1 }))
        .build()
        .unwrap();
    let child = ServiceCollection::new()
        .singleton(|_| Arc::new(common::MyService { value: 2 }))
        .build()
        .unwrap();
    let w = ServiceProviderWrapper::new(child, root);
    assert_eq!(w.get::<common::MyService>().unwrap().value, 2);
    assert_eq!(w.get_optional::<common::MyService>().unwrap().value, 2);
}

#[test]
fn wrapper_get_optional_none() {
    let root = ServiceCollection::new().build().unwrap();
    let child = ServiceCollection::new().build().unwrap();
    let w = ServiceProviderWrapper::new(child, root);
    assert!(w.get_optional::<common::MyService>().is_none());
}

#[test]
fn wrapper_get_keyed_child_priority() {
    let root = ServiceCollection::new()
        .keyed_singleton("k", |_| Arc::new(common::MyService { value: 10 }))
        .build()
        .unwrap();
    let child = ServiceCollection::new()
        .keyed_singleton("k", |_| Arc::new(common::MyService { value: 20 }))
        .build()
        .unwrap();
    let w = ServiceProviderWrapper::new(child, root);
    assert_eq!(w.get_keyed::<common::MyService>("k").unwrap().value, 20);
}

#[test]
fn wrapper_get_named_not_found() {
    let root = ServiceCollection::new().build().unwrap();
    let child = ServiceCollection::new().build().unwrap();
    let w = ServiceProviderWrapper::new(child, root);
    assert!(w.get_named::<common::MyService>("nothing").is_none());
}

#[test]
fn wrapper_get_named_any_not_found() {
    let root = ServiceCollection::new().build().unwrap();
    let child = ServiceCollection::new().build().unwrap();
    let w = ServiceProviderWrapper::new(child, root);
    assert!(w.get_named_any("nothing").is_none());
}

#[test]
fn singleton_keyed_via_get_resolves_default() {
    // Registering multiple keyed entries should NOT create a default.
    // get() should not find anything.
    let p = ServiceCollection::new()
        .keyed_singleton("a", |_| Arc::new(common::MyService { value: 1 }))
        .build()
        .unwrap();
    // get_keyed should work for the registered key
    assert_eq!(p.get_keyed::<common::MyService>("a").unwrap().value, 1);
}

#[test]
fn get_panics_on_missing() {
    let p = ServiceCollection::new().build().unwrap();
    let result: Result<Arc<common::MyService>, RdiError> = p.get();
    assert!(result.is_err());
}

#[test]
fn get_keyed_panics_on_missing() {
    let p = ServiceCollection::new().build().unwrap();
    let result: Result<Arc<common::MyService>, RdiError> = p.get_keyed("missing");
    assert!(result.is_err());
}

#[test]
fn rdi_register_named_and_remove_via_provider() {
    let p = ServiceCollection::new().build().unwrap();
    let svc: Arc<dyn std::any::Any + Send + Sync> =
        Arc::new(Arc::new(common::MyService { value: 5 }));
    p.rdi_register_named("test", svc.clone());
    assert!(p.get_named_any("test").is_some());
    p.rdi_remove_named("test");
    assert!(p.get_named_any("test").is_none());
}

#[test]
fn scope_get_panics_on_missing() {
    let p = ServiceCollection::new().build().unwrap();
    let scope = p.scope();
    let result: Result<Arc<common::MyService>, RdiError> = scope.get();
    assert!(result.is_err());
}

#[test]
fn scope_get_keyed_panics_on_missing() {
    let p = ServiceCollection::new().build().unwrap();
    let scope = p.scope();
    let result: Result<Arc<common::MyService>, RdiError> = scope.get_keyed("nonexistent");
    assert!(result.is_err());
}

#[test]
fn module_singleton_with_trait() {
    #[rust_dix::module]
    mod svc {
        rust_dix::register!(singleton: dyn super::common::IPlugin => super::common::TestPlugin);
    }
    let p = svc::__rdi_build_provider_svc().unwrap();
    let plugin: Arc<dyn common::IPlugin> = p.get::<dyn common::IPlugin>().unwrap();
    assert_eq!(plugin.name(), "test_plugin");
}

#[test]
fn module_scoped_lifetime() {
    #[rust_dix::module]
    mod scoped_mod {
        rust_dix::register!(scoped: super::common::MyService);
    }
    let p = Arc::new(scoped_mod::__rdi_build_provider_scoped_mod().unwrap());
    let scope = p.scope();
    let _svc: Arc<common::MyService> = scope.get().unwrap();
}

#[test]
fn module_transient_lifetime() {
    #[rust_dix::module]
    mod trans_mod {
        rust_dix::register!(transient: super::common::MyService);
    }
    let p = trans_mod::__rdi_build_provider_trans_mod().unwrap();
    let _svc: Arc<common::MyService> = p.get().unwrap();
}

#[test]
fn module_multiple_registrations() {
    #[rust_dix::module]
    mod multi {
        rust_dix::register!(singleton: super::common::Logger);
        rust_dix::register!(singleton: super::common::MyService);
    }
    let p = multi::__rdi_build_provider_multi().unwrap();
    let _log: Arc<common::Logger> = p.get().unwrap();
    let _svc: Arc<common::MyService> = p.get().unwrap();
}

#[test]
fn module_keyed_singleton() {
    #[rust_dix::module]
    mod keyed_mod {
        rust_dix::register!(keyed "x": singleton: super::common::MyService);
    }
    let p = keyed_mod::__rdi_build_provider_keyed_mod().unwrap();
    let svc: Arc<common::MyService> = p.get_keyed("x").unwrap();
    assert_eq!(svc.value, 0);
}

#[test]
fn pattern_get_all_trait_objects() {
    let p = ServiceCollection::new()
        .singleton::<dyn common::IPlugin>(|_| Arc::new(common::TestPlugin))
        .singleton::<dyn common::IPlugin>(|_| Arc::new(common::AltPlugin))
        .build()
        .unwrap();
    let all: Vec<Arc<dyn common::IPlugin>> = p.get_all();
    assert_eq!(all.len(), 2);
}