Skip to main content

dependency_injector/
provider.rs

1//! Provider traits for dependency injection
2//!
3//! These traits define what types can be injected and how they behave.
4
5use std::any::TypeId;
6use std::sync::Arc;
7
8/// Marker trait for types that can be injected via the DI container.
9///
10/// This is automatically implemented for all types that are `Send + Sync + 'static`.
11/// You never need to implement this manually.
12///
13/// # Examples
14///
15/// ```rust
16/// // Any type that is Send + Sync + 'static works automatically
17/// #[derive(Clone)]
18/// struct MyService {
19///     name: String,
20/// }
21///
22/// // No impl needed - it just works!
23/// ```
24pub trait Injectable: Send + Sync + 'static {
25    /// Returns the TypeId of this type (for internal use)
26    #[inline]
27    fn type_id_of() -> TypeId
28    where
29        Self: Sized,
30    {
31        TypeId::of::<Self>()
32    }
33
34    /// Returns the type name for debugging
35    #[inline]
36    fn type_name_of() -> &'static str
37    where
38        Self: Sized,
39    {
40        std::any::type_name::<Self>()
41    }
42}
43
44// Blanket implementation - everything that's Send + Sync + 'static is Injectable
45impl<T: Send + Sync + 'static> Injectable for T {}
46
47/// Backward compatibility alias
48pub trait Provider: Injectable {}
49impl<T: Injectable> Provider for T {}
50
51/// Service lifetime specification
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
53pub enum Lifetime {
54    /// Single instance shared across all resolves
55    #[default]
56    Singleton,
57
58    /// New instance created lazily on first access, then shared
59    Lazy,
60
61    /// New instance created on every resolve
62    Transient,
63
64    /// One instance per scope
65    Scoped,
66}
67
68/// Registration information for a provider (used by module system)
69#[derive(Clone)]
70pub struct ProviderRegistration {
71    /// TypeId of the provider
72    pub type_id: TypeId,
73    /// Human-readable type name
74    pub type_name: &'static str,
75    /// Registration function
76    pub register_fn: Arc<dyn Fn(&crate::Container) + Send + Sync>,
77}
78
79impl ProviderRegistration {
80    /// Create a new registration for type T
81    #[inline]
82    pub fn new<T: Injectable>(register_fn: fn(&crate::Container)) -> Self {
83        Self {
84            type_id: TypeId::of::<T>(),
85            type_name: std::any::type_name::<T>(),
86            register_fn: Arc::new(register_fn),
87        }
88    }
89
90    /// Create from a singleton value
91    ///
92    /// The value is captured by the registration, and `register_fn` registers
93    /// a clone of it as a singleton on the container it is given.
94    pub fn singleton<T: Injectable + Clone>(value: T) -> Self {
95        Self {
96            type_id: TypeId::of::<T>(),
97            type_name: std::any::type_name::<T>(),
98            register_fn: Arc::new(move |container: &crate::Container| {
99                container.singleton(value.clone());
100            }),
101        }
102    }
103}
104
105impl std::fmt::Debug for ProviderRegistration {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_struct("ProviderRegistration")
108            .field("type_id", &self.type_id)
109            .field("type_name", &self.type_name)
110            .finish()
111    }
112}
113
114/// Helper macro to create a provider registration
115#[macro_export]
116macro_rules! provider {
117    ($type:ty, $factory:expr) => {
118        $crate::ProviderRegistration {
119            type_id: std::any::TypeId::of::<$type>(),
120            type_name: std::any::type_name::<$type>(),
121            register_fn: $crate::Arc::new(|container: &$crate::Container| {
122                container.singleton($factory);
123            }),
124        }
125    };
126    (lazy $type:ty, $factory:expr) => {
127        $crate::ProviderRegistration {
128            type_id: std::any::TypeId::of::<$type>(),
129            type_name: std::any::type_name::<$type>(),
130            register_fn: $crate::Arc::new(|container: &$crate::Container| {
131                container.lazy($factory);
132            }),
133        }
134    };
135    (transient $type:ty, $factory:expr) => {
136        $crate::ProviderRegistration {
137            type_id: std::any::TypeId::of::<$type>(),
138            type_name: std::any::type_name::<$type>(),
139            register_fn: $crate::Arc::new(|container: &$crate::Container| {
140                container.transient($factory);
141            }),
142        }
143    };
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use crate::Container;
150
151    #[derive(Clone)]
152    struct TestService {
153        value: String,
154    }
155
156    #[test]
157    fn test_singleton_registration_registers_value() {
158        let registration = ProviderRegistration::singleton(TestService {
159            value: "provided".into(),
160        });
161
162        assert_eq!(registration.type_id, TypeId::of::<TestService>());
163
164        // Applying the registration actually registers the captured value
165        let container = Container::new();
166        (registration.register_fn)(&container);
167
168        let service = container.get::<TestService>().unwrap();
169        assert_eq!(service.value, "provided");
170    }
171}