rust-dix 0.6.0

rust-dix: A Rust dependency injection framework inspired by Microsoft.Extensions.DependencyInjection
Documentation
//! 多实现注册测试 - 验证一个 trait 多个实现的多态支持

use rust_dix::*;
use std::sync::Arc;

// ── trait 定义 ──

trait IPayGateway: Send + Sync {
    fn pay(&self, amount: f64) -> String;
    fn name(&self) -> &'static str;
}

trait ILogOutput: Send + Sync {
    fn write(&self, msg: &str) -> String;
}

// ── 实现 ──

struct WechatPay;
impl IPayGateway for WechatPay {
    fn pay(&self, amount: f64) -> String {
        format!("微信支付: ¥{}", amount)
    }
    fn name(&self) -> &'static str {
        "wechat"
    }
}

struct Alipay;
impl IPayGateway for Alipay {
    fn pay(&self, amount: f64) -> String {
        format!("支付宝: ¥{}", amount)
    }
    fn name(&self) -> &'static str {
        "alipay"
    }
}

struct CreditCardPay;
impl IPayGateway for CreditCardPay {
    fn pay(&self, amount: f64) -> String {
        format!("信用卡: ¥{}", amount)
    }
    fn name(&self) -> &'static str {
        "credit"
    }
}

struct FileLog;
impl ILogOutput for FileLog {
    fn write(&self, msg: &str) -> String {
        format!("[FILE] {}", msg)
    }
}

struct ConsoleLog;
impl ILogOutput for ConsoleLog {
    fn write(&self, msg: &str) -> String {
        format!("[CONSOLE] {}", msg)
    }
}

// ── 测试 ──

#[test]
fn default_plus_keyed() {
    // 默认 + keyed 组合
    let p = ServiceCollection::new()
        .singleton::<dyn IPayGateway>(|_| Arc::new(WechatPay)) // 默认
        .keyed_singleton::<dyn IPayGateway>("wechat", |_| Arc::new(WechatPay))
        .keyed_singleton::<dyn IPayGateway>("alipay", |_| Arc::new(Alipay))
        .keyed_singleton::<dyn IPayGateway>("credit", |_| Arc::new(CreditCardPay))
        .build()
        .unwrap();

    // 默认解析
    let default: Arc<dyn IPayGateway> = p.get().unwrap();
    assert_eq!(default.name(), "wechat");

    // keyed 解析
    let alipay: Arc<dyn IPayGateway> = p.get_keyed("alipay").unwrap();
    assert_eq!(alipay.name(), "alipay");

    let credit: Arc<dyn IPayGateway> = p.get_keyed("credit").unwrap();
    assert_eq!(credit.name(), "credit");

    // 批量获取
    let all: Vec<Arc<dyn IPayGateway>> = p.get_all();
    assert_eq!(all.len(), 4); // 1 default + 3 keyed
}

#[test]
fn all_keyed_no_default() {
    // 全 keyed,无默认
    let p = ServiceCollection::new()
        .keyed_singleton::<dyn IPayGateway>("wechat", |_| Arc::new(WechatPay))
        .keyed_singleton::<dyn IPayGateway>("alipay", |_| Arc::new(Alipay))
        .build()
        .unwrap();

    // 无默认,get() panic
    let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        let _: Arc<dyn IPayGateway> = p.get().unwrap();
    }));
    assert!(result.is_err());

    // 只能通过 keyed 获取
    let wechat: Arc<dyn IPayGateway> = p.get_keyed("wechat").unwrap();
    assert_eq!(wechat.name(), "wechat");

    // get_all 获取所有
    let all: Vec<Arc<dyn IPayGateway>> = p.get_all();
    assert_eq!(all.len(), 2);
}

#[test]
fn get_all_returns_all_implementations() {
    let p = ServiceCollection::new()
        .keyed_singleton::<dyn ILogOutput>("file", |_| Arc::new(FileLog))
        .keyed_singleton::<dyn ILogOutput>("console", |_| Arc::new(ConsoleLog))
        .build()
        .unwrap();

    let all: Vec<Arc<dyn ILogOutput>> = p.get_all();
    assert_eq!(all.len(), 2);

    // 验证所有实现都能调用
    let results: Vec<String> = all.iter().map(|l| l.write("test")).collect();
    assert!(results.iter().any(|r| r.contains("[FILE]")));
    assert!(results.iter().any(|r| r.contains("[CONSOLE]")));
}

#[test]
fn keyed_transient_creates_new_instance() {
    use std::sync::atomic::{AtomicUsize, Ordering};
    static CNT: AtomicUsize = AtomicUsize::new(0);

    struct Counter;
    impl IPayGateway for Counter {
        fn pay(&self, amount: f64) -> String {
            CNT.fetch_add(1, Ordering::SeqCst);
            format!("count: {}", amount)
        }
        fn name(&self) -> &'static str {
            "counter"
        }
    }

    let p = ServiceCollection::new()
        .keyed_transient::<dyn IPayGateway>("counter", |_| Arc::new(Counter))
        .build()
        .unwrap();

    let c1: Arc<dyn IPayGateway> = p.get_keyed("counter").unwrap();
    let c2: Arc<dyn IPayGateway> = p.get_keyed("counter").unwrap();
    c1.pay(1.0);
    c2.pay(2.0);

    // 验证:pay 被调用 2 次(c1 和 c2 各一次)
    assert_eq!(CNT.load(Ordering::SeqCst), 2);
}

#[test]
fn strategy_pattern_runtime_switch() {
    // 策略模式:运行时路由
    let p = ServiceCollection::new()
        .singleton::<dyn IPayGateway>(|_| Arc::new(WechatPay)) // 默认
        .keyed_singleton::<dyn IPayGateway>("wechat", |_| Arc::new(WechatPay))
        .keyed_singleton::<dyn IPayGateway>("alipay", |_| Arc::new(Alipay))
        .build()
        .unwrap();

    fn pay_by_method(p: &ServiceProvider, method: &str, amount: f64) -> String {
        p.try_get_keyed::<dyn IPayGateway>(method)
            .unwrap_or_else(|| p.get().unwrap())
            .pay(amount)
    }

    // 指定方法
    assert!(pay_by_method(&p, "alipay", 100.0).contains("支付宝"));
    assert!(pay_by_method(&p, "wechat", 100.0).contains("微信支付"));

    // 未知方法回退默认
    assert!(pay_by_method(&p, "unknown", 100.0).contains("微信支付"));
}