shaku 0.6.3

Compile Time Dependency Injection for Rust
Documentation
use shaku::{
    module, Component, HasComponent, HasComponents, HasProvider, Interface, Module,
    ModuleBuildContext, Provider,
};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

static LAZY_ORDERED_BUILDS: AtomicUsize = AtomicUsize::new(0);
static LAZY_ORDERED_PROVIDER_BUILDS: AtomicUsize = AtomicUsize::new(0);

trait Foo: Interface {
    fn name(&self) -> &'static str;
}

trait Runner: Interface {
    fn names(&self) -> Vec<&'static str>;
}

trait ProvidedRunner {
    fn names(&self) -> Vec<&'static str>;
}

#[derive(Component)]
#[shaku(interface = Foo)]
struct FooAlpha;

impl Foo for FooAlpha {
    fn name(&self) -> &'static str {
        "alpha"
    }
}

#[derive(Component)]
#[shaku(interface = Foo)]
struct FooBeta;

impl Foo for FooBeta {
    fn name(&self) -> &'static str {
        "beta"
    }
}

#[derive(Component)]
#[shaku(interface = Runner)]
struct RunnerImpl {
    #[shaku(inject)]
    foos: Vec<Arc<dyn Foo>>,
}

impl Runner for RunnerImpl {
    fn names(&self) -> Vec<&'static str> {
        self.foos.iter().map(|component| component.name()).collect()
    }
}

#[derive(Provider)]
#[shaku(interface = ProvidedRunner)]
struct ProvidedRunnerImpl {
    #[shaku(inject)]
    foos: Vec<Arc<dyn Foo>>,
}

impl ProvidedRunner for ProvidedRunnerImpl {
    fn names(&self) -> Vec<&'static str> {
        self.foos.iter().map(|component| component.name()).collect()
    }
}

struct LazyFooAlpha;

impl Foo for LazyFooAlpha {
    fn name(&self) -> &'static str {
        "alpha"
    }
}

impl<M: Module> Component<M> for LazyFooAlpha {
    type Interface = dyn Foo;
    type Parameters = ();

    fn build(_: &mut ModuleBuildContext<M>, _: Self::Parameters) -> Box<Self::Interface> {
        LAZY_ORDERED_BUILDS.fetch_add(1, Ordering::SeqCst);
        Box::new(Self)
    }
}

struct LazyProvidedFooAlpha;

impl Foo for LazyProvidedFooAlpha {
    fn name(&self) -> &'static str {
        "alpha"
    }
}

impl<M: Module> Component<M> for LazyProvidedFooAlpha {
    type Interface = dyn Foo;
    type Parameters = ();

    fn build(_: &mut ModuleBuildContext<M>, _: Self::Parameters) -> Box<Self::Interface> {
        LAZY_ORDERED_PROVIDER_BUILDS.fetch_add(1, Ordering::SeqCst);
        Box::new(Self)
    }
}

module! {
    TestOrderedModule {
        components = [
            #[ordered(dyn Foo)]
            FooAlpha,
            #[ordered(dyn Foo)]
            FooBeta,
            RunnerImpl
        ],
        providers = [ProvidedRunnerImpl]
    }
}

module! {
    TestLazyOrderedModule {
        components = [
            #[lazy]
            #[ordered(dyn Foo)]
            LazyFooAlpha
        ],
        providers = []
    }
}

module! {
    TestLazyOrderedProviderModule {
        components = [
            #[lazy]
            #[ordered(dyn Foo)]
            LazyProvidedFooAlpha
        ],
        providers = [ProvidedRunnerImpl]
    }
}

#[test]
fn resolves_ordered_component_vec() {
    let module = TestOrderedModule::builder().build();

    let runner: Arc<dyn Runner> = module.resolve();
    assert_eq!(runner.names(), vec!["alpha", "beta"]);

    let foos: &[Arc<dyn Foo>] = module.resolve_all();
    assert_eq!(
        foos.iter()
            .map(|component| component.name())
            .collect::<Vec<_>>(),
        vec!["alpha", "beta"]
    );

    let first = module.resolve_all().as_ptr();
    let second = module.resolve_all().as_ptr();
    assert_eq!(first, second);
}

#[test]
fn provider_can_inject_ordered_component_vec() {
    let module = TestOrderedModule::builder().build();

    let runner: Box<dyn ProvidedRunner> = module.provide().unwrap();
    assert_eq!(runner.names(), vec!["alpha", "beta"]);
}

#[test]
fn lazy_ordered_component_builds_on_first_collection_resolution() {
    LAZY_ORDERED_BUILDS.store(0, Ordering::SeqCst);
    let module = TestLazyOrderedModule::builder().build();

    assert_eq!(LAZY_ORDERED_BUILDS.load(Ordering::SeqCst), 0);

    let foos: &[Arc<dyn Foo>] = module.resolve_all();
    assert_eq!(LAZY_ORDERED_BUILDS.load(Ordering::SeqCst), 1);
    assert_eq!(
        foos.iter()
            .map(|component| component.name())
            .collect::<Vec<_>>(),
        vec!["alpha"]
    );

    let _foos_again: &[Arc<dyn Foo>] = module.resolve_all();
    assert_eq!(LAZY_ORDERED_BUILDS.load(Ordering::SeqCst), 1);
}

#[test]
fn lazy_ordered_component_builds_once_when_injected_into_provider() {
    LAZY_ORDERED_PROVIDER_BUILDS.store(0, Ordering::SeqCst);
    let module = TestLazyOrderedProviderModule::builder().build();

    assert_eq!(LAZY_ORDERED_PROVIDER_BUILDS.load(Ordering::SeqCst), 0);

    let runner: Box<dyn ProvidedRunner> = module.provide().unwrap();
    assert_eq!(LAZY_ORDERED_PROVIDER_BUILDS.load(Ordering::SeqCst), 1);
    assert_eq!(runner.names(), vec!["alpha"]);

    let runner_again: Box<dyn ProvidedRunner> = module.provide().unwrap();
    assert_eq!(LAZY_ORDERED_PROVIDER_BUILDS.load(Ordering::SeqCst), 1);
    assert_eq!(runner_again.names(), vec!["alpha"]);
}