runtime_injector 0.4.0

Runtime dependency injection container
Documentation
#![allow(clippy::blacklisted_name)]

use crate::{
    constant, interface, InjectError, InjectResult, Injector, IntoSingleton,
    IntoTransient, RequestInfo, ServiceInfo, Services, Svc, TypedProvider,
};
use std::sync::Mutex;

#[derive(Default)]
struct Svc1(pub i32);

struct Svc2 {
    pub dep1: Svc<Svc1>,
}

impl Svc2 {
    pub fn new(dep1: Svc<Svc1>) -> Self {
        Svc2 { dep1 }
    }
}

struct Svc3 {
    pub dep1: Svc<Svc1>,
    pub dep2: Svc<Svc2>,
}

impl Svc3 {
    pub fn new(dep1: Svc<Svc1>, dep2: Svc<Svc2>) -> Self {
        Svc3 { dep1, dep2 }
    }
}

#[test]
fn can_make_svc1() {
    let mut builder = Injector::builder();
    builder.provide(Svc1::default.transient());

    let injector = builder.build();
    let _service: Svc<Svc1> = injector.get().unwrap();
}

#[test]
fn cant_make_svc1_when_no_provider() {
    let injector = Injector::builder().build();
    let svc: InjectResult<Svc<Svc1>> = injector.get();
    match svc {
        Err(InjectError::MissingProvider { service_info })
            if service_info == ServiceInfo::of::<Svc1>() => {}
        Err(error) => Err(error).unwrap(),
        Ok(_) => unreachable!(),
    }

    let svc: Option<Svc<Svc1>> = injector.get().unwrap();
    match svc {
        None => {}
        Some(_) => panic!("service should not have been created"),
    }
}

#[test]
fn can_make_svc3() {
    let mut builder = Injector::builder();
    builder.provide(Svc1::default.transient());
    builder.provide(Svc2::new.transient());
    builder.provide(Svc3::new.transient());

    let injector = builder.build();
    let _service: Svc<Svc3> = injector.get().unwrap();
}

#[test]
fn cant_make_svc3_when_no_provider_for_dependency() {
    let mut builder = Injector::builder();
    builder.provide(Svc2::new.transient());
    builder.provide(Svc3::new.transient());

    let injector = builder.build();
    match injector.get::<Svc<Svc3>>() {
        Err(InjectError::MissingDependency {
            dependency_info, ..
        }) if dependency_info == ServiceInfo::of::<Svc1>() => {}
        Err(error) => Err(error).unwrap(),
        Ok(_) => unreachable!("service should not be able to be activated"),
    }
}

#[test]
fn singleton() {
    type Counter = Mutex<i32>;

    fn make_svc1(counter: Svc<Counter>) -> Svc1 {
        let mut counter = counter.lock().unwrap();
        *counter += 1;
        Svc1(*counter)
    }

    let mut builder = Injector::builder();
    builder.provide((|| Mutex::new(0)).singleton());
    builder.provide(make_svc1.transient());
    builder.provide(Svc2::new.transient());
    builder.provide(Svc3::new.transient());

    let injector = builder.build();
    let svc1: Svc<Svc1> = injector.get().unwrap();
    let svc2: Svc<Svc2> = injector.get().unwrap();
    let svc3: Svc<Svc3> = injector.get().unwrap();

    assert_ne!(svc1.0, svc2.dep1.0);
    assert_ne!(svc1.0, svc3.dep1.0);
    assert_ne!(svc2.dep1.0, svc3.dep1.0);
}

#[test]
fn constants() {
    type Counter = Mutex<i32>;

    fn make_svc1(counter: Svc<Counter>) -> Svc1 {
        let mut counter = counter.lock().unwrap();
        *counter += 1;
        Svc1(*counter)
    }

    let mut builder = Injector::builder();
    builder.provide(constant(Mutex::new(0)));
    builder.provide(make_svc1.transient());
    builder.provide(Svc2::new.transient());
    builder.provide(Svc3::new.transient());

    let injector = builder.build();
    let svc1: Svc<Svc1> = injector.get().unwrap();
    let svc2: Svc<Svc2> = injector.get().unwrap();
    let svc3: Svc<Svc3> = injector.get().unwrap();

    assert_ne!(svc1.0, svc2.dep1.0);
    assert_ne!(svc1.0, svc3.dep1.0);
    assert_ne!(svc2.dep1.0, svc3.dep1.0);
}

#[test]
fn interfaces() {
    #[cfg(feature = "rc")]
    pub trait Foo {
        fn bar(&self) -> i32;
    }

    #[cfg(feature = "arc")]
    pub trait Foo: Send + Sync {
        fn bar(&self) -> i32;
    }

    interface!(
        dyn Foo = [
            Svc1,
            #[cfg(test)]
            Svc2,
            #[cfg(not(test))]
            Svc3,
        ]
    );

    impl Foo for Svc1 {
        fn bar(&self) -> i32 {
            4
        }
    }

    impl Foo for Svc2 {
        fn bar(&self) -> i32 {
            5
        }
    }

    struct Svc4 {
        pub foo: Svc<dyn Foo>,
    }

    impl Svc4 {
        pub fn new(foo: Svc<dyn Foo>) -> Self {
            Svc4 { foo }
        }
    }

    // Svc1
    let mut builder = Injector::builder();
    builder.provide(Svc1::default.transient().with_interface::<dyn Foo>());

    let injector = builder.build();
    let svc: Svc<dyn Foo> = injector.get().unwrap();

    assert_eq!(4, svc.bar());

    // Svc2
    let mut builder = Injector::builder();
    builder.provide(Svc1::default.transient());
    builder.provide(Svc2::new.transient().with_interface::<dyn Foo>());

    let injector = builder.build();
    let svc: Svc<dyn Foo> = injector.get().unwrap();

    assert_eq!(5, svc.bar());

    // Svc4
    let mut builder = Injector::builder();
    builder.provide(Svc1::default.transient());
    builder.provide(Svc2::new.transient().with_interface::<dyn Foo>());
    builder.provide(Svc4::new.transient());

    let injector = builder.build();
    let svc: Svc<Svc4> = injector.get().unwrap();

    assert_eq!(5, svc.foo.bar());
}

#[test]
fn multi_injection() {
    #[cfg(feature = "rc")]
    trait Foo {}

    #[cfg(feature = "arc")]
    trait Foo: Send + Sync {}

    impl Foo for Svc1 {}
    impl Foo for Svc2 {}
    impl Foo for Svc3 {}

    interface!(dyn Foo = [Svc1, Svc2, Svc3]);

    let mut builder = Injector::builder();
    builder.provide(Svc1::default.transient().with_interface::<dyn Foo>());

    let injector = builder.build();
    let mut foos: Services<dyn Foo> = injector.get().unwrap();
    assert_eq!(1, foos.len());

    let foos: Vec<Svc<dyn Foo>> =
        foos.get_all().collect::<InjectResult<_>>().unwrap();
    assert_eq!(1, foos.len());
}

#[test]
fn injector_returns_error_on_cycles() {
    struct Foo(Svc<Bar>);
    struct Bar(Svc<Foo>);

    let mut builder = Injector::builder();
    builder.provide(Foo.singleton());
    builder.provide(Bar.singleton());

    let injector = builder.build();
    match injector.get::<Svc<Foo>>() {
        Err(InjectError::CycleDetected {
            service_info,
            cycle,
        }) if service_info == ServiceInfo::of::<Foo>() => {
            assert_eq!(3, cycle.len());
            assert_eq!(ServiceInfo::of::<Foo>(), cycle[0]);
            assert_eq!(ServiceInfo::of::<Bar>(), cycle[1]);
            assert_eq!(ServiceInfo::of::<Foo>(), cycle[2]);
        }
        Ok(_) => panic!("somehow created a Foo with a cyclic dependency"),
        Err(error) => Err(error).unwrap(),
    }
}

#[test]
fn request_info_has_correct_path() {
    struct Foo(Svc<Bar>, RequestInfo);
    struct Bar(RequestInfo);

    let mut builder = Injector::builder();
    builder.provide(Foo.transient());
    builder.provide(Bar.transient());

    let injector = builder.build();
    let bar: Svc<Bar> = injector.get().unwrap();
    let foo: Svc<Foo> = injector.get().unwrap();

    assert_eq!(&[ServiceInfo::of::<Bar>()], bar.0.service_path());
    assert_eq!(&[ServiceInfo::of::<Foo>()], foo.1.service_path());
    assert_eq!(
        &[ServiceInfo::of::<Foo>(), ServiceInfo::of::<Bar>()],
        foo.0 .0.service_path()
    );
}