1use crate::container::autowiring::{DependencyResolver, Injectable};
4use crate::container::binding::ServiceBinder;
5use crate::container::descriptor::ServiceId;
6use crate::container::ioc_container::IocContainer;
7use crate::errors::CoreError;
8use std::sync::Arc;
9
10#[derive(Default)]
20pub struct SqliteUserRepository {
21 connection_string: String,
22}
23
24impl SqliteUserRepository {
25 pub fn find_user(&self, id: u32) -> Option<User> {
26 println!(
27 "Finding user {} from SQLite at {}",
28 id, self.connection_string
29 );
30 Some(User {
31 id,
32 name: format!("User {}", id),
33 email: format!("user{}@example.com", id),
34 })
35 }
36}
37
38#[derive(Default)]
39pub struct PostgresProductRepository;
40
41impl PostgresProductRepository {
42 pub fn find_product(&self, id: u32) -> Option<Product> {
43 println!("Finding product {} from Postgres", id);
44 Some(Product {
45 id,
46 name: format!("Product {}", id),
47 price: 29.99,
48 })
49 }
50}
51
52#[derive(Default)]
53pub struct SmtpEmailService {
54 smtp_server: String,
55}
56
57impl SmtpEmailService {
58 pub fn send_email(&self, to: &str, subject: &str, body: &str) -> Result<(), String> {
59 println!(
60 "Sending email via {} to {}: {} - {}",
61 self.smtp_server, to, subject, body
62 );
63 Ok(())
64 }
65}
66
67#[derive(Default)]
68pub struct PaymentProcessor;
69
70impl PaymentProcessor {
71 pub fn process_payment(&self, amount: f64) -> Result<String, String> {
72 println!("Processing payment of ${:.2}", amount);
73 Ok(format!("payment_id_{}", (amount * 100.0) as u32))
74 }
75}
76
77#[derive(Default)]
79pub struct MetricsCollector;
80
81impl MetricsCollector {
82 pub fn record_metric(&self, name: &str, value: f64) {
83 println!("Recording metric: {} = {}", name, value);
84 }
85}
86
87#[derive(Debug)]
89pub struct User {
90 pub id: u32,
91 pub name: String,
92 pub email: String,
93}
94
95#[derive(Debug)]
96pub struct Product {
97 pub id: u32,
98 pub name: String,
99 pub price: f64,
100}
101
102#[derive(Debug)]
103pub struct Order {
104 pub user: User,
105 pub product: Product,
106 pub payment_id: String,
107}
108
109pub struct OrderService {
111 user_repo: Arc<SqliteUserRepository>,
112 product_repo: Arc<PostgresProductRepository>,
113 email_service: Arc<SmtpEmailService>,
114 payment_processor: Arc<PaymentProcessor>,
115 metrics: Option<Arc<MetricsCollector>>, }
117
118impl OrderService {
119 pub fn new(
120 user_repo: Arc<SqliteUserRepository>,
121 product_repo: Arc<PostgresProductRepository>,
122 email_service: Arc<SmtpEmailService>,
123 payment_processor: Arc<PaymentProcessor>,
124 metrics: Option<Arc<MetricsCollector>>,
125 ) -> Self {
126 Self {
127 user_repo,
128 product_repo,
129 email_service,
130 payment_processor,
131 metrics,
132 }
133 }
134
135 pub fn create_order(&self, user_id: u32, product_id: u32) -> Result<Order, String> {
136 if let Some(metrics) = &self.metrics {
138 metrics.record_metric("order.created", 1.0);
139 }
140
141 let user = self.user_repo.find_user(user_id).ok_or("User not found")?;
143
144 let product = self
145 .product_repo
146 .find_product(product_id)
147 .ok_or("Product not found")?;
148
149 let payment_id = self.payment_processor.process_payment(product.price)?;
151
152 self.email_service.send_email(
154 &user.email,
155 "Order Confirmation",
156 &format!("Your order for {} has been confirmed!", product.name),
157 )?;
158
159 Ok(Order {
160 user,
161 product,
162 payment_id,
163 })
164 }
165}
166
167impl Injectable for OrderService {
169 fn dependencies() -> Vec<ServiceId> {
170 vec![
171 ServiceId::of::<SqliteUserRepository>(),
172 ServiceId::of::<PostgresProductRepository>(),
173 ServiceId::of::<SmtpEmailService>(),
174 ServiceId::of::<PaymentProcessor>(),
175 ServiceId::of::<MetricsCollector>(), ]
177 }
178
179 fn create<R: DependencyResolver>(resolver: &R) -> Result<Self, CoreError> {
180 let user_repo = resolver.resolve::<SqliteUserRepository>()?;
181 let product_repo = resolver.resolve::<PostgresProductRepository>()?;
182 let email_service = resolver.resolve::<SmtpEmailService>()?;
183 let payment_processor = resolver.resolve::<PaymentProcessor>()?;
184 let metrics = resolver.try_resolve::<MetricsCollector>(); Ok(OrderService::new(
187 user_repo,
188 product_repo,
189 email_service,
190 payment_processor,
191 metrics,
192 ))
193 }
194}
195
196pub fn run_autowiring_example() -> Result<(), CoreError> {
198 println!("š IoC Phase 2: Constructor Injection & Auto-wiring Example");
199 println!("============================================================");
200
201 let mut container = IocContainer::new();
203
204 container
206 .bind::<SqliteUserRepository, SqliteUserRepository>()
207 .bind::<PostgresProductRepository, PostgresProductRepository>()
208 .bind::<SmtpEmailService, SmtpEmailService>()
209 .bind::<PaymentProcessor, PaymentProcessor>()
210 .bind::<MetricsCollector, MetricsCollector>() .bind_injectable::<OrderService>(); container.build()?;
215
216 println!(
217 "\nš¦ Container built successfully with {} services",
218 container.service_count()
219 );
220
221 let order_service = container.resolve_injectable::<OrderService>()?;
223
224 println!("\nšÆ OrderService resolved with auto-wiring!");
225 println!(" Dependencies automatically injected:");
226 println!(" - SqliteUserRepository");
227 println!(" - PostgresProductRepository");
228 println!(" - SmtpEmailService");
229 println!(" - PaymentProcessor");
230 println!(" - MetricsCollector (optional)");
231
232 println!("\nš Creating order...");
234 let order = order_service
235 .create_order(1, 101)
236 .map_err(|e| CoreError::InvalidServiceDescriptor { message: e })?;
237
238 println!("\nā
Order created successfully!");
239 println!(" User: {} ({})", order.user.name, order.user.email);
240 println!(
241 " Product: {} - ${:.2}",
242 order.product.name, order.product.price
243 );
244 println!(" Payment ID: {}", order.payment_id);
245
246 println!("\nš§ Testing without optional dependency...");
248 let mut container_minimal = IocContainer::new();
249 container_minimal
250 .bind::<SqliteUserRepository, SqliteUserRepository>()
251 .bind::<PostgresProductRepository, PostgresProductRepository>()
252 .bind::<SmtpEmailService, SmtpEmailService>()
253 .bind::<PaymentProcessor, PaymentProcessor>();
254 container_minimal.build()?;
258
259 let user_repo = container_minimal.resolve::<SqliteUserRepository>()?;
261 let product_repo = container_minimal.resolve::<PostgresProductRepository>()?;
262 let email_service = container_minimal.resolve::<SmtpEmailService>()?;
263 let payment_processor = container_minimal.resolve::<PaymentProcessor>()?;
264 let metrics = container_minimal.try_resolve::<MetricsCollector>();
265
266 let order_service_minimal = OrderService::new(
267 user_repo,
268 product_repo,
269 email_service,
270 payment_processor,
271 metrics,
272 );
273 println!("ā
OrderService created even without optional MetricsCollector!");
274
275 let _order2 = order_service_minimal
276 .create_order(2, 102)
277 .map_err(|e| CoreError::InvalidServiceDescriptor { message: e })?;
278 println!("ā
Order processing works without metrics service");
279
280 Ok(())
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_autowiring_example() {
289 run_autowiring_example().expect("Autowiring example should work");
290 }
291
292 #[test]
293 fn test_dependency_resolution() {
294 let deps = OrderService::dependencies();
295 assert_eq!(deps.len(), 5);
296 assert!(deps.contains(&ServiceId::of::<SqliteUserRepository>()));
297 assert!(deps.contains(&ServiceId::of::<PostgresProductRepository>()));
298 assert!(deps.contains(&ServiceId::of::<SmtpEmailService>()));
299 assert!(deps.contains(&ServiceId::of::<PaymentProcessor>()));
300 assert!(deps.contains(&ServiceId::of::<MetricsCollector>()));
301 }
302
303 #[test]
304 fn test_optional_dependency_handling() {
305 let mut container = IocContainer::new();
306
307 container
309 .bind::<SqliteUserRepository, SqliteUserRepository>()
310 .bind::<PostgresProductRepository, PostgresProductRepository>()
311 .bind::<SmtpEmailService, SmtpEmailService>()
312 .bind::<PaymentProcessor, PaymentProcessor>();
313
314 container.build().unwrap();
315
316 let user_repo = container.resolve::<SqliteUserRepository>().unwrap();
318 let product_repo = container.resolve::<PostgresProductRepository>().unwrap();
319 let email_service = container.resolve::<SmtpEmailService>().unwrap();
320 let payment_processor = container.resolve::<PaymentProcessor>().unwrap();
321 let metrics = container.try_resolve::<MetricsCollector>(); assert!(
324 metrics.is_none(),
325 "MetricsCollector should not be available"
326 );
327
328 let order_service = OrderService::new(
329 user_repo,
330 product_repo,
331 email_service,
332 payment_processor,
333 metrics,
334 );
335 let order = order_service.create_order(1, 101).unwrap();
336
337 assert_eq!(order.user.id, 1);
338 assert_eq!(order.product.id, 101);
339 assert!(!order.payment_id.is_empty());
340 }
341
342 #[test]
343 fn test_complex_dependency_graph() {
344 let mut container = IocContainer::new();
345
346 container
348 .bind::<SqliteUserRepository, SqliteUserRepository>()
349 .bind::<PostgresProductRepository, PostgresProductRepository>()
350 .bind::<SmtpEmailService, SmtpEmailService>()
351 .bind::<PaymentProcessor, PaymentProcessor>()
352 .bind::<MetricsCollector, MetricsCollector>()
353 .bind_injectable::<OrderService>();
354
355 container.build().unwrap();
356
357 assert!(container.resolve::<SqliteUserRepository>().is_ok());
359 assert!(container.resolve::<PostgresProductRepository>().is_ok());
360 assert!(container.resolve::<SmtpEmailService>().is_ok());
361 assert!(container.resolve::<PaymentProcessor>().is_ok());
362 assert!(container.resolve::<MetricsCollector>().is_ok());
363 assert!(container.resolve_injectable::<OrderService>().is_ok());
364 }
365}