use rust_dix::*;
use std::sync::Arc;
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() {
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");
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); }
#[test]
fn all_keyed_no_default() {
let p = ServiceCollection::new()
.keyed_singleton::<dyn IPayGateway>("wechat", |_| Arc::new(WechatPay))
.keyed_singleton::<dyn IPayGateway>("alipay", |_| Arc::new(Alipay))
.build()
.unwrap();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _: Arc<dyn IPayGateway> = p.get().unwrap();
}));
assert!(result.is_err());
let wechat: Arc<dyn IPayGateway> = p.get_keyed("wechat").unwrap();
assert_eq!(wechat.name(), "wechat");
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);
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("微信支付"));
}