noema 0.4.0

Noema IOC and DI framework for Rust
Documentation
//! Dependency injection: compile-time registration and unified resolution.
//!
//! See the full macro and syntax guide in [`di.md`](../docs/di.md).

#[macro_use]
mod macros;

mod keyed;
mod resolve;
mod traits;

pub use keyed::Keyed;
pub use resolve::{ResolveTarget, resolve};
pub use traits::{KeyedResolver, ManyResolver};

#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use crate::core::{Container, Injectable, Resolver};
    use crate::{arc_dyn, dependency, dependency_as, dependency_as_many, dependency_keyed_as, Keyed, resolve};

    #[test]
    fn dependency_as_resolves_trait() {
        trait Greeter: Send + Sync {
            fn greet(&self) -> &'static str;
        }
        struct Hello;
        impl Greeter for Hello {
            fn greet(&self) -> &'static str {
                "hello"
            }
        }
        impl Injectable<Container> for Hello {
            fn inject(_: &Container) -> Self {
                Hello
            }
        }

        dependency_as!(singleton, Greeter: Hello);
        let g = resolve::<dyn Greeter + Send + Sync>();
        assert_eq!(g.greet(), "hello");
    }

    #[test]
    fn transient_concrete_creates_new_instances() {
        struct IdGen(u32);
        impl Injectable<Container> for IdGen {
            fn inject(_: &Container) -> Self {
                IdGen(7)
            }
        }
        dependency!(transient, IdGen);

        let a = resolve::<IdGen>();
        let b = resolve::<IdGen>();
        assert!(!Arc::ptr_eq(&a, &b));
    }

    #[test]
    fn many_and_keyed_registrations() {
        trait Processor: Send + Sync {
            fn id(&self) -> i32;
        }

        struct ProcA;
        impl Processor for ProcA {
            fn id(&self) -> i32 {
                1
            }
        }
        impl Injectable<Container> for ProcA {
            fn inject(_: &Container) -> Self {
                ProcA
            }
        }

        struct ProcB;
        impl Processor for ProcB {
            fn id(&self) -> i32 {
                2
            }
        }
        impl Injectable<Container> for ProcB {
            fn inject(_: &Container) -> Self {
                ProcB
            }
        }

        struct Stripe;
        #[derive(Clone, Copy, PartialEq)]
        enum PayKey {
            Stripe,
            Paypal,
        }
        trait Pay: Send + Sync {
            fn name(&self) -> &'static str;
        }
        impl Pay for Stripe {
            fn name(&self) -> &'static str {
                "stripe"
            }
        }
        impl Injectable<Container> for Stripe {
            fn inject(_: &Container) -> Self {
                Stripe
            }
        }

        dependency_as_many!(Processor: [(singleton, ProcA), (singleton, ProcB)]);
        dependency_keyed_as!(PayKey, Pay: [(PayKey::Stripe, singleton, Stripe)]);

        let all = resolve::<Arc<[Arc<dyn Processor + Send + Sync>]>>();
        assert_eq!(all.len(), 2);

        let keyed = resolve::<Keyed<PayKey, dyn Pay + Send + Sync>>();
        let pay = keyed.one(PayKey::Stripe).expect("stripe");
        assert_eq!(pay.name(), "stripe");

        assert!(keyed.one(PayKey::Paypal).is_none());
    }

    #[test]
    fn transient_many_batch_creates_fresh_instances() {
        trait Token: Send + Sync {
            fn value(&self) -> u32;
        }

        struct TokenA;
        impl Token for TokenA {
            fn value(&self) -> u32 {
                1
            }
        }
        impl Injectable<Container> for TokenA {
            fn inject(_: &Container) -> Self {
                TokenA
            }
        }

        struct TokenB;
        impl Token for TokenB {
            fn value(&self) -> u32 {
                2
            }
        }
        impl Injectable<Container> for TokenB {
            fn inject(_: &Container) -> Self {
                TokenB
            }
        }

        dependency_as_many!(Token: [(transient, TokenA), (transient, TokenB)]);

        let first = resolve::<Arc<[Arc<dyn Token + Send + Sync>]>>();
        let second = resolve::<Arc<[Arc<dyn Token + Send + Sync>]>>();
        assert_eq!(first.len(), 2);
        assert_eq!(second.len(), 2);
        assert!(!Arc::ptr_eq(&first[0], &second[0]));
        assert!(!Arc::ptr_eq(&first[1], &second[1]));
    }

    #[test]
    fn mixed_lifecycle_many_and_keyed() {
        trait Worker: Send + Sync {
            fn tag(&self) -> u8;
        }

        struct CachedWorker;
        impl Worker for CachedWorker {
            fn tag(&self) -> u8 {
                1
            }
        }
        impl Injectable<Container> for CachedWorker {
            fn inject(_: &Container) -> Self {
                CachedWorker
            }
        }

        struct FreshWorker;
        impl Worker for FreshWorker {
            fn tag(&self) -> u8 {
                2
            }
        }
        impl Injectable<Container> for FreshWorker {
            fn inject(_: &Container) -> Self {
                FreshWorker
            }
        }

        #[derive(Clone, Copy, PartialEq)]
        enum WorkerKey {
            Cached,
            Fresh,
        }

        struct CachedKeyed;
        impl Worker for CachedKeyed {
            fn tag(&self) -> u8 {
                10
            }
        }
        impl Injectable<Container> for CachedKeyed {
            fn inject(_: &Container) -> Self {
                CachedKeyed
            }
        }

        struct FreshKeyed;
        impl Worker for FreshKeyed {
            fn tag(&self) -> u8 {
                20
            }
        }
        impl Injectable<Container> for FreshKeyed {
            fn inject(_: &Container) -> Self {
                FreshKeyed
            }
        }

        dependency_as_many!(Worker: [(singleton, CachedWorker), (transient, FreshWorker)]);
        dependency_keyed_as!(WorkerKey, Worker: [
            (WorkerKey::Cached, singleton, CachedKeyed),
            (WorkerKey::Fresh, transient, FreshKeyed),
        ]);

        let many_a = resolve::<Arc<[Arc<dyn Worker + Send + Sync>]>>();
        let many_b = resolve::<Arc<[Arc<dyn Worker + Send + Sync>]>>();
        assert_eq!(many_a.len(), 2);
        assert!(Arc::ptr_eq(&many_a[0], &many_b[0]));
        assert!(!Arc::ptr_eq(&many_a[1], &many_b[1]));

        let keyed = resolve::<Keyed<WorkerKey, dyn Worker + Send + Sync>>();
        let cached_a = keyed.one(WorkerKey::Cached).expect("cached");
        let cached_b = keyed.one(WorkerKey::Cached).expect("cached");
        let fresh_a = keyed.one(WorkerKey::Fresh).expect("fresh");
        let fresh_b = keyed.one(WorkerKey::Fresh).expect("fresh");
        assert!(Arc::ptr_eq(&cached_a, &cached_b));
        assert!(!Arc::ptr_eq(&fresh_a, &fresh_b));
    }

    #[test]
    fn injectable_derive_resolves_dependencies() {
        trait Clock: Send + Sync {
            fn now(&self) -> u32;
        }
        struct SystemClock;
        impl Clock for SystemClock {
            fn now(&self) -> u32 {
                42
            }
        }
        impl Injectable<Container> for SystemClock {
            fn inject(_: &Container) -> Self {
                SystemClock
            }
        }
        dependency_as!(singleton, Clock: SystemClock);

        #[derive(Injectable)]
        struct Service {
            clock: arc_dyn!(Clock),
        }

        let svc = Service::inject(&Container);
        assert_eq!(svc.clock.now(), 42);
    }
}