dependency_injector/
verified.rs

1//! Verified Service Providers
2//!
3//! This module provides traits for services that declare their dependencies
4//! at compile time, enabling static verification of dependency graphs.
5//!
6//! # Features
7//!
8//! - **`Service` trait**: Declare dependencies and creation logic
9//! - **`ServiceProvider` trait**: Auto-register services with their factories
10//! - **Compile-time cycle detection**: The type system prevents circular deps
11//!
12//! # Example
13//!
14//! ```rust
15//! use dependency_injector::verified::{Service, ServiceProvider};
16//! use dependency_injector::Container;
17//! use std::sync::Arc;
18//!
19//! #[derive(Clone)]
20//! struct Database {
21//!     url: String,
22//! }
23//!
24//! impl Service for Database {
25//!     type Dependencies = ();
26//!
27//!     fn create(_deps: Self::Dependencies) -> Self {
28//!         Database { url: "postgres://localhost".into() }
29//!     }
30//! }
31//!
32//! #[derive(Clone)]
33//! struct UserRepository {
34//!     db: Arc<Database>,
35//! }
36//!
37//! impl Service for UserRepository {
38//!     type Dependencies = Arc<Database>;
39//!
40//!     fn create(db: Self::Dependencies) -> Self {
41//!         UserRepository { db }
42//!     }
43//! }
44//!
45//! // Auto-register with dependencies resolved
46//! let container = Container::new();
47//! container.provide::<Database>();
48//! container.provide::<UserRepository>();
49//!
50//! let repo = container.get::<UserRepository>().unwrap();
51//! ```
52
53use crate::{Container, Injectable};
54use std::sync::Arc;
55
56// =============================================================================
57// Service Trait
58// =============================================================================
59
60/// A service that declares its dependencies at compile time.
61///
62/// The `Dependencies` associated type specifies what the service needs,
63/// and `create` defines how to construct the service given those dependencies.
64///
65/// # Supported Dependency Types
66///
67/// - `()` - No dependencies
68/// - `Arc<T>` - Single required dependency
69/// - `(Arc<A>, Arc<B>)` - Multiple dependencies (tuples up to 12)
70/// - `Option<Arc<T>>` - Optional dependency
71///
72/// # Example
73///
74/// ```rust
75/// use dependency_injector::verified::Service;
76/// use std::sync::Arc;
77///
78/// #[derive(Clone)]
79/// struct Config {
80///     debug: bool,
81/// }
82///
83/// impl Service for Config {
84///     type Dependencies = ();
85///
86///     fn create(_: ()) -> Self {
87///         Config { debug: false }
88///     }
89/// }
90///
91/// #[derive(Clone)]
92/// struct Logger {
93///     config: Arc<Config>,
94/// }
95///
96/// impl Service for Logger {
97///     type Dependencies = Arc<Config>;
98///
99///     fn create(config: Arc<Config>) -> Self {
100///         Logger { config }
101///     }
102/// }
103/// ```
104pub trait Service: Injectable + Sized {
105    /// The dependencies required to create this service.
106    ///
107    /// Use `()` for no dependencies, `Arc<T>` for one, or tuples for multiple.
108    type Dependencies: Resolvable;
109
110    /// Create a new instance given the resolved dependencies.
111    fn create(deps: Self::Dependencies) -> Self;
112}
113
114// =============================================================================
115// Resolvable Trait - Dependencies that can be resolved from a container
116// =============================================================================
117
118/// Trait for types that can be resolved from a container.
119///
120/// This is automatically implemented for:
121/// - `()` - No dependencies
122/// - `Arc<T>` - Single service
123/// - Tuples of `Arc<T>` - Multiple services
124/// - `Option<Arc<T>>` - Optional service
125pub trait Resolvable: Sized {
126    /// Resolve this dependency from the container.
127    ///
128    /// Returns `None` if any required dependency is missing.
129    fn resolve(container: &Container) -> Option<Self>;
130}
131
132// No dependencies
133impl Resolvable for () {
134    #[inline]
135    fn resolve(_container: &Container) -> Option<Self> {
136        Some(())
137    }
138}
139
140// Single dependency
141impl<T: Injectable> Resolvable for Arc<T> {
142    #[inline]
143    fn resolve(container: &Container) -> Option<Self> {
144        container.try_get::<T>()
145    }
146}
147
148// Optional dependency
149impl<T: Injectable> Resolvable for Option<Arc<T>> {
150    #[inline]
151    fn resolve(container: &Container) -> Option<Self> {
152        Some(container.try_get::<T>())
153    }
154}
155
156// Tuple implementations (2-12 elements)
157macro_rules! impl_resolvable_tuple {
158    ($($T:ident),+) => {
159        impl<$($T: Injectable),+> Resolvable for ($(Arc<$T>,)+) {
160            #[inline]
161            fn resolve(container: &Container) -> Option<Self> {
162                Some(($(container.try_get::<$T>()?,)+))
163            }
164        }
165    };
166}
167
168impl_resolvable_tuple!(A, B);
169impl_resolvable_tuple!(A, B, C);
170impl_resolvable_tuple!(A, B, C, D);
171impl_resolvable_tuple!(A, B, C, D, E);
172impl_resolvable_tuple!(A, B, C, D, E, F);
173impl_resolvable_tuple!(A, B, C, D, E, F, G);
174impl_resolvable_tuple!(A, B, C, D, E, F, G, H);
175impl_resolvable_tuple!(A, B, C, D, E, F, G, H, I);
176impl_resolvable_tuple!(A, B, C, D, E, F, G, H, I, J);
177impl_resolvable_tuple!(A, B, C, D, E, F, G, H, I, J, K);
178impl_resolvable_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
179
180// =============================================================================
181// ServiceProvider Trait - Auto-registration
182// =============================================================================
183
184/// Extension trait for containers to auto-register services.
185pub trait ServiceProvider {
186    /// Register a service using its `Service` implementation.
187    ///
188    /// The service will be created lazily on first access, with dependencies
189    /// resolved from the container.
190    ///
191    /// # Panics
192    ///
193    /// The created factory will panic at runtime if dependencies are missing.
194    /// For compile-time safety, use the typed builder API.
195    ///
196    /// # Example
197    ///
198    /// ```rust
199    /// use dependency_injector::{Container, verified::{Service, ServiceProvider}};
200    ///
201    /// #[derive(Clone)]
202    /// struct MyService;
203    ///
204    /// impl Service for MyService {
205    ///     type Dependencies = ();
206    ///     fn create(_: ()) -> Self { MyService }
207    /// }
208    ///
209    /// let container = Container::new();
210    /// container.provide::<MyService>();
211    ///
212    /// let service = container.get::<MyService>().unwrap();
213    /// ```
214    fn provide<T: Service>(&self);
215
216    /// Register a service as a singleton with pre-resolved dependencies.
217    ///
218    /// Dependencies are resolved immediately, not lazily.
219    ///
220    /// # Returns
221    ///
222    /// `true` if all dependencies were resolved and the service was registered,
223    /// `false` if any dependency was missing.
224    fn provide_singleton<T: Service>(&self) -> bool;
225
226    /// Register a service as transient.
227    ///
228    /// A new instance is created on every resolution.
229    fn provide_transient<T: Service>(&self);
230}
231
232impl ServiceProvider for Container {
233    #[inline]
234    fn provide<T: Service>(&self) {
235        let container = self.clone();
236        self.lazy(move || {
237            let deps = T::Dependencies::resolve(&container)
238                .expect("Failed to resolve dependencies for service");
239            T::create(deps)
240        });
241    }
242
243    #[inline]
244    fn provide_singleton<T: Service>(&self) -> bool {
245        if let Some(deps) = T::Dependencies::resolve(self) {
246            self.singleton(T::create(deps));
247            true
248        } else {
249            false
250        }
251    }
252
253    #[inline]
254    fn provide_transient<T: Service>(&self) {
255        let container = self.clone();
256        self.transient(move || {
257            let deps = T::Dependencies::resolve(&container)
258                .expect("Failed to resolve dependencies for transient service");
259            T::create(deps)
260        });
261    }
262}
263
264// =============================================================================
265// ServiceModule - Group related services
266// =============================================================================
267
268/// A module that groups related service registrations.
269///
270/// # Example
271///
272/// ```rust
273/// use dependency_injector::{Container, verified::{Service, ServiceModule, ServiceProvider}};
274///
275/// #[derive(Clone)]
276/// struct Database;
277///
278/// impl Service for Database {
279///     type Dependencies = ();
280///     fn create(_: ()) -> Self { Database }
281/// }
282///
283/// #[derive(Clone)]
284/// struct Cache;
285///
286/// impl Service for Cache {
287///     type Dependencies = ();
288///     fn create(_: ()) -> Self { Cache }
289/// }
290///
291/// struct DataModule;
292///
293/// impl ServiceModule for DataModule {
294///     fn register(container: &Container) {
295///         container.provide::<Database>();
296///         container.provide::<Cache>();
297///     }
298/// }
299///
300/// let container = Container::new();
301/// DataModule::register(&container);
302///
303/// assert!(container.contains::<Database>());
304/// assert!(container.contains::<Cache>());
305/// ```
306pub trait ServiceModule {
307    /// Register all services in this module.
308    fn register(container: &Container);
309}
310
311// =============================================================================
312// Dependency Graph Helpers
313// =============================================================================
314
315/// Trait for extracting dependency type information.
316///
317/// This is mainly useful for debugging and visualization.
318pub trait DependencyInfo {
319    /// Get the type names of all dependencies.
320    fn dependency_names() -> Vec<&'static str>;
321}
322
323impl DependencyInfo for () {
324    fn dependency_names() -> Vec<&'static str> {
325        vec![]
326    }
327}
328
329impl<T: Injectable> DependencyInfo for Arc<T> {
330    fn dependency_names() -> Vec<&'static str> {
331        vec![std::any::type_name::<T>()]
332    }
333}
334
335impl<T: Injectable> DependencyInfo for Option<Arc<T>> {
336    fn dependency_names() -> Vec<&'static str> {
337        vec![std::any::type_name::<T>()]
338    }
339}
340
341// Tuple implementations for DependencyInfo
342macro_rules! impl_dependency_info_tuple {
343    ($($T:ident),+) => {
344        impl<$($T: Injectable),+> DependencyInfo for ($(Arc<$T>,)+) {
345            fn dependency_names() -> Vec<&'static str> {
346                vec![$(std::any::type_name::<$T>()),+]
347            }
348        }
349    };
350}
351
352impl_dependency_info_tuple!(A, B);
353impl_dependency_info_tuple!(A, B, C);
354impl_dependency_info_tuple!(A, B, C, D);
355impl_dependency_info_tuple!(A, B, C, D, E);
356impl_dependency_info_tuple!(A, B, C, D, E, F);
357
358// =============================================================================
359// Compile-Time Cycle Detection (Documentation Only)
360// =============================================================================
361
362// Note: Full compile-time cycle detection requires either:
363// 1. A procedural macro that analyzes the full dependency graph
364// 2. Unstable Rust features (specialization, const generics)
365//
366// The current approach provides partial protection:
367// - The `Service` trait requires explicit dependency declaration
368// - The `TypedBuilder::with_dependencies` method verifies deps exist
369// - Runtime errors are caught when resolving missing dependencies
370//
371// For complete compile-time cycle detection, consider:
372// - Using the `#[derive(Service)]` macro which can analyze dependencies
373// - Using the typed builder API which tracks registrations
374//
375// Future: When Rust's type system supports it, we can add full cycle detection.
376
377// =============================================================================
378// Tests
379// =============================================================================
380
381#[cfg(test)]
382mod tests {
383    use super::*;
384
385    #[derive(Clone)]
386    struct Config {
387        debug: bool,
388    }
389
390    impl Service for Config {
391        type Dependencies = ();
392
393        fn create(_: ()) -> Self {
394            Config { debug: true }
395        }
396    }
397
398    #[derive(Clone)]
399    struct Database {
400        url: String,
401    }
402
403    impl Service for Database {
404        type Dependencies = Arc<Config>;
405
406        fn create(config: Arc<Config>) -> Self {
407            Database {
408                url: if config.debug {
409                    "debug://localhost".into()
410                } else {
411                    "prod://server".into()
412                },
413            }
414        }
415    }
416
417    #[derive(Clone)]
418    struct Cache {
419        size: usize,
420    }
421
422    impl Service for Cache {
423        type Dependencies = ();
424
425        fn create(_: ()) -> Self {
426            Cache { size: 1024 }
427        }
428    }
429
430    #[derive(Clone)]
431    struct UserRepository {
432        db: Arc<Database>,
433        cache: Arc<Cache>,
434    }
435
436    impl Service for UserRepository {
437        type Dependencies = (Arc<Database>, Arc<Cache>);
438
439        fn create((db, cache): (Arc<Database>, Arc<Cache>)) -> Self {
440            UserRepository { db, cache }
441        }
442    }
443
444    #[test]
445    fn test_service_no_deps() {
446        let container = Container::new();
447        container.provide::<Config>();
448
449        let config = container.get::<Config>().unwrap();
450        assert!(config.debug);
451    }
452
453    #[test]
454    fn test_service_single_dep() {
455        let container = Container::new();
456        container.provide::<Config>();
457        container.provide::<Database>();
458
459        let db = container.get::<Database>().unwrap();
460        assert_eq!(db.url, "debug://localhost");
461    }
462
463    #[test]
464    fn test_service_multiple_deps() {
465        let container = Container::new();
466        container.provide::<Config>();
467        container.provide::<Database>();
468        container.provide::<Cache>();
469        container.provide::<UserRepository>();
470
471        let repo = container.get::<UserRepository>().unwrap();
472        assert_eq!(repo.db.url, "debug://localhost");
473        assert_eq!(repo.cache.size, 1024);
474    }
475
476    #[test]
477    fn test_provide_singleton() {
478        let container = Container::new();
479        container.provide::<Config>();
480
481        // Should succeed
482        let result = container.provide_singleton::<Database>();
483        assert!(result);
484
485        let db = container.get::<Database>().unwrap();
486        assert_eq!(db.url, "debug://localhost");
487    }
488
489    #[test]
490    fn test_provide_singleton_missing_dep() {
491        let container = Container::new();
492
493        // Should fail - Config not registered
494        let result = container.provide_singleton::<Database>();
495        assert!(!result);
496    }
497
498    #[test]
499    fn test_provide_transient() {
500        use std::sync::atomic::{AtomicU32, Ordering};
501
502        static COUNTER: AtomicU32 = AtomicU32::new(0);
503
504        #[derive(Clone)]
505        struct Counter(u32);
506
507        impl Service for Counter {
508            type Dependencies = ();
509
510            fn create(_: ()) -> Self {
511                Counter(COUNTER.fetch_add(1, Ordering::SeqCst))
512            }
513        }
514
515        let container = Container::new();
516        container.provide_transient::<Counter>();
517
518        let c1 = container.get::<Counter>().unwrap();
519        let c2 = container.get::<Counter>().unwrap();
520
521        assert_ne!(c1.0, c2.0);
522    }
523
524    #[test]
525    fn test_optional_dependency() {
526        #[derive(Clone)]
527        struct OptionalCache;
528
529        #[derive(Clone)]
530        struct ServiceWithOptional {
531            cache: Option<Arc<OptionalCache>>,
532        }
533
534        impl Service for ServiceWithOptional {
535            type Dependencies = Option<Arc<OptionalCache>>;
536
537            fn create(cache: Option<Arc<OptionalCache>>) -> Self {
538                ServiceWithOptional { cache }
539            }
540        }
541
542        let container = Container::new();
543        container.provide::<ServiceWithOptional>();
544
545        let svc = container.get::<ServiceWithOptional>().unwrap();
546        assert!(svc.cache.is_none());
547
548        // Now register the optional dep
549        let container2 = Container::new();
550        container2.singleton(OptionalCache);
551        container2.provide::<ServiceWithOptional>();
552
553        let svc2 = container2.get::<ServiceWithOptional>().unwrap();
554        assert!(svc2.cache.is_some());
555    }
556
557    #[test]
558    fn test_dependency_info() {
559        assert_eq!(
560            <() as DependencyInfo>::dependency_names(),
561            Vec::<&str>::new()
562        );
563        assert_eq!(
564            <Arc<Config> as DependencyInfo>::dependency_names(),
565            vec!["dependency_injector::verified::tests::Config"]
566        );
567        assert_eq!(
568            <(Arc<Database>, Arc<Cache>) as DependencyInfo>::dependency_names().len(),
569            2
570        );
571    }
572
573    #[test]
574    fn test_service_module() {
575        struct TestModule;
576
577        impl ServiceModule for TestModule {
578            fn register(container: &Container) {
579                container.provide::<Config>();
580                container.provide::<Cache>();
581            }
582        }
583
584        let container = Container::new();
585        TestModule::register(&container);
586
587        assert!(container.contains::<Config>());
588        assert!(container.contains::<Cache>());
589    }
590}
591