use singleton_registry::define_registry;
use std::sync::Arc;
define_registry!(services);
trait Logger: Send + Sync {
fn log(&self, message: &str);
fn name(&self) -> &str;
}
trait Notifier: Send + Sync {
fn notify(&self, recipient: &str, message: &str);
fn service_type(&self) -> &str;
}
struct ConsoleLogger;
impl Logger for ConsoleLogger {
fn log(&self, message: &str) {
println!("[CONSOLE] {}", message);
}
fn name(&self) -> &str {
"ConsoleLogger"
}
}
struct FileLogger {
path: String,
}
impl Logger for FileLogger {
fn log(&self, message: &str) {
println!("[FILE:{}] {}", self.path, message);
}
fn name(&self) -> &str {
"FileLogger"
}
}
struct EmailNotifier {
smtp_server: String,
}
impl Notifier for EmailNotifier {
fn notify(&self, recipient: &str, message: &str) {
println!(
"[EMAIL via {}] To: {} - {}",
self.smtp_server, recipient, message
);
}
fn service_type(&self) -> &str {
"Email"
}
}
struct SmsNotifier {
api_key: String,
}
impl Notifier for SmsNotifier {
fn notify(&self, recipient: &str, message: &str) {
println!(
"[SMS via API:{}] To: {} - {}",
&self.api_key[..8],
recipient,
message
);
}
fn service_type(&self) -> &str {
"SMS"
}
}
fn process_order(order_id: u32) {
let logger: Arc<dyn Logger> = services::get_cloned().unwrap();
let notifier: Arc<dyn Notifier> = services::get_cloned().unwrap();
logger.log(&format!("Processing order #{}", order_id));
logger.log("Validating payment...");
logger.log("Order confirmed!");
notifier.notify(
"customer@example.com",
&format!("Order #{} confirmed!", order_id),
);
}
fn main() {
println!("=== singleton-registry: Trait Contracts ===\n");
println!("1. Registering initial implementations...");
services::register(Arc::new(ConsoleLogger) as Arc<dyn Logger>);
services::register(Arc::new(EmailNotifier {
smtp_server: "smtp.example.com".to_string(),
}) as Arc<dyn Notifier>);
println!(" Logger: ConsoleLogger");
println!(" Notifier: EmailNotifier");
println!("\n2. Processing order with initial implementations...\n");
process_order(1001);
println!("\n3. Swapping to different implementations...");
services::register(Arc::new(FileLogger {
path: "/var/log/app.log".to_string(),
}) as Arc<dyn Logger>);
services::register(Arc::new(SmsNotifier {
api_key: "sk_live_abc123xyz789".to_string(),
}) as Arc<dyn Notifier>);
println!(" Logger: FileLogger");
println!(" Notifier: SmsNotifier");
println!("\n4. Processing another order with new implementations...\n");
process_order(1002);
println!("\n5. Verifying current implementations...");
let logger: Arc<dyn Logger> = services::get_cloned().unwrap();
let notifier: Arc<dyn Notifier> = services::get_cloned().unwrap();
println!(" Current Logger: {}", logger.name());
println!(" Current Notifier: {}", notifier.service_type());
println!("\n=== Example Complete ===");
println!("The registry enables contract-based DI:");
println!(" - Business logic depends on traits (Logger, Notifier)");
println!(" - Implementations can be swapped without changing consumer code");
println!(" - Perfect for testing: register mocks during test setup");
}