#[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);
}
}