fluxdi 1.2.2

FluxDI - Semi-Automatic Dependency Injector
Documentation
use std::sync::atomic::{AtomicUsize, Ordering};

use fluxdi::{ErrorKind, Injector, Provider, Shared};

trait PipelineStep: Send + Sync {
    fn id(&self) -> &'static str;
}

struct StepA;
impl PipelineStep for StepA {
    fn id(&self) -> &'static str {
        "a"
    }
}

struct StepB;
impl PipelineStep for StepB {
    fn id(&self) -> &'static str {
        "b"
    }
}

#[test]
fn set_resolve_preserves_registration_order() {
    let injector = Injector::root();

    injector.provide_into_set::<dyn PipelineStep>(Provider::singleton(|_| {
        Shared::new(StepA) as Shared<dyn PipelineStep>
    }));
    injector.provide_into_set::<dyn PipelineStep>(Provider::singleton(|_| {
        Shared::new(StepB) as Shared<dyn PipelineStep>
    }));

    let steps = injector.try_resolve_all::<dyn PipelineStep>().unwrap();
    let ids: Vec<&'static str> = steps.iter().map(|step| step.id()).collect();

    assert_eq!(ids, vec!["a", "b"]);
}

#[test]
fn parent_set_bindings_are_resolved_before_child_bindings() {
    let parent = Injector::root();
    parent.provide_into_set::<dyn PipelineStep>(Provider::singleton(|_| {
        Shared::new(StepA) as Shared<dyn PipelineStep>
    }));

    let child = Injector::child(Shared::new(parent.clone()));
    child.provide_into_set::<dyn PipelineStep>(Provider::singleton(|_| {
        Shared::new(StepB) as Shared<dyn PipelineStep>
    }));

    let steps = child.try_resolve_all::<dyn PipelineStep>().unwrap();
    let ids: Vec<&'static str> = steps.iter().map(|step| step.id()).collect();

    assert_eq!(ids, vec!["a", "b"]);
}

#[derive(Debug)]
struct Marker {
    source: &'static str,
    serial: usize,
}

#[test]
fn set_resolution_honors_scope_caching_rules() {
    static ROOT_CREATED: AtomicUsize = AtomicUsize::new(0);
    static MODULE_CREATED: AtomicUsize = AtomicUsize::new(0);
    static TRANSIENT_CREATED: AtomicUsize = AtomicUsize::new(0);

    ROOT_CREATED.store(0, Ordering::SeqCst);
    MODULE_CREATED.store(0, Ordering::SeqCst);
    TRANSIENT_CREATED.store(0, Ordering::SeqCst);

    let injector = Injector::root();
    injector.provide_into_set::<Marker>(Provider::root(|_| {
        Shared::new(Marker {
            source: "root",
            serial: ROOT_CREATED.fetch_add(1, Ordering::SeqCst),
        })
    }));
    injector.provide_into_set::<Marker>(Provider::singleton(|_| {
        Shared::new(Marker {
            source: "module",
            serial: MODULE_CREATED.fetch_add(1, Ordering::SeqCst),
        })
    }));
    injector.provide_into_set::<Marker>(Provider::transient(|_| {
        Shared::new(Marker {
            source: "transient",
            serial: TRANSIENT_CREATED.fetch_add(1, Ordering::SeqCst),
        })
    }));

    let first = injector.try_resolve_all::<Marker>().unwrap();
    let second = injector.try_resolve_all::<Marker>().unwrap();

    assert_eq!(first.len(), 3);
    assert_eq!(second.len(), 3);
    assert_eq!(first[0].source, "root");
    assert_eq!(first[1].source, "module");
    assert_eq!(first[2].source, "transient");

    assert!(Shared::ptr_eq(&first[0], &second[0]));
    assert!(Shared::ptr_eq(&first[1], &second[1]));
    assert!(!Shared::ptr_eq(&first[2], &second[2]));

    assert_eq!(first[0].serial, 0);
    assert_eq!(first[1].serial, 0);
    assert_eq!(first[2].serial, 0);
    assert_eq!(second[2].serial, 1);

    assert_eq!(ROOT_CREATED.load(Ordering::SeqCst), 1);
    assert_eq!(MODULE_CREATED.load(Ordering::SeqCst), 1);
    assert_eq!(TRANSIENT_CREATED.load(Ordering::SeqCst), 2);
}

#[cfg(feature = "async-factory")]
#[test]
fn set_with_async_provider_requires_async_resolve_api() {
    use futures::executor::block_on;

    let injector = Injector::root();
    injector.provide_into_set::<String>(Provider::singleton(|_| Shared::new("sync".to_string())));
    injector.provide_into_set::<String>(Provider::singleton_async(|_| async {
        Shared::new("async".to_string())
    }));

    let sync_err = match injector.try_resolve_all::<String>() {
        Ok(_) => panic!("expected sync set resolve to fail for async provider"),
        Err(err) => err,
    };
    assert_eq!(sync_err.kind, ErrorKind::AsyncFactoryRequiresAsyncResolve);

    let values = block_on(injector.try_resolve_all_async::<String>()).unwrap();
    let values: Vec<&str> = values.iter().map(|value| value.as_str()).collect();
    assert_eq!(values, vec!["sync", "async"]);
}