dependency_injector/lib.rs
1//! # Armature DI - High-Performance Dependency Injection for Rust
2//!
3//! A lightning-fast, type-safe dependency injection container optimized for
4//! real-world web framework usage.
5//!
6//! ## Features
7//!
8//! - โก **Lock-free** - Uses `DashMap` for concurrent access without blocking
9//! - ๐ **Type-safe** - Compile-time type checking with zero runtime overhead
10//! - ๐ **Zero-config** - Any `Send + Sync + 'static` type is automatically injectable
11//! - ๐ **Scoped containers** - Hierarchical scopes with full parent chain resolution
12//! - ๐ญ **Lazy singletons** - Services created on first access
13//! - โป๏ธ **Transient services** - Fresh instance on every resolve
14//! - ๐งต **Thread-local cache** - Hot path optimization for frequently accessed services
15//! - ๐ **Observable** - Optional tracing integration with JSON or pretty output
16//! - โณ **Async singletons** - Optional `async` feature adds `lazy_async`/`get_async` (tokio)
17//!
18//! ## Quick Start
19//!
20//! ```rust
21//! use dependency_injector::Container;
22//!
23//! // Any Send + Sync + 'static type works - no boilerplate!
24//! #[derive(Clone)]
25//! struct Database {
26//! url: String,
27//! }
28//!
29//! #[derive(Clone)]
30//! struct UserService {
31//! db: Database,
32//! }
33//!
34//! let container = Container::new();
35//!
36//! // Register services
37//! container.singleton(Database { url: "postgres://localhost".into() });
38//! container.singleton(UserService {
39//! db: Database { url: "postgres://localhost".into() }
40//! });
41//!
42//! // Resolve - returns Arc<T> for zero-copy sharing
43//! let db = container.get::<Database>().unwrap();
44//! let users = container.get::<UserService>().unwrap();
45//! ```
46//!
47//! ## Service Lifetimes
48//!
49//! ```rust
50//! use dependency_injector::Container;
51//! use std::sync::atomic::{AtomicU64, Ordering};
52//!
53//! static COUNTER: AtomicU64 = AtomicU64::new(0);
54//!
55//! #[derive(Clone, Default)]
56//! struct Config { debug: bool }
57//!
58//! #[derive(Clone)]
59//! struct RequestId(u64);
60//!
61//! let container = Container::new();
62//!
63//! // Singleton - one instance, shared everywhere
64//! container.singleton(Config { debug: true });
65//!
66//! // Lazy singleton - created on first access
67//! container.lazy(|| Config { debug: false });
68//!
69//! // Transient - new instance every time
70//! container.transient(|| RequestId(COUNTER.fetch_add(1, Ordering::SeqCst)));
71//! ```
72//!
73//! ## Scoped Containers
74//!
75//! ```rust
76//! use dependency_injector::Container;
77//!
78//! #[derive(Clone)]
79//! struct AppConfig { name: String }
80//!
81//! #[derive(Clone)]
82//! struct RequestContext { id: String }
83//!
84//! // Root container with app-wide services
85//! let root = Container::new();
86//! root.singleton(AppConfig { name: "MyApp".into() });
87//!
88//! // Per-request scope - inherits from root
89//! let request_scope = root.scope();
90//! request_scope.singleton(RequestContext { id: "req-123".into() });
91//!
92//! // Request scope can access root services
93//! assert!(request_scope.contains::<AppConfig>());
94//! assert!(request_scope.contains::<RequestContext>());
95//!
96//! // Root cannot access request-scoped services
97//! assert!(!root.contains::<RequestContext>());
98//! ```
99//!
100//! ## Performance
101//!
102//! - **Lock-free reads**: Using `DashMap` for ~10x faster concurrent access vs `RwLock`
103//! - **AHash**: Faster hashing for `TypeId` keys
104//! - **Thread-local cache**: Avoid map lookups for hot services
105//! - **Zero allocation resolve**: Returns `Arc<T>` directly, no cloning
106
107#[cfg(feature = "async")]
108pub mod async_support;
109mod container;
110mod error;
111mod factory;
112#[cfg(feature = "ffi")]
113pub mod ffi;
114#[cfg(feature = "logging")]
115pub mod logging;
116mod provider;
117mod scope;
118mod storage;
119pub mod typed;
120pub mod verified;
121
122// Re-export FrozenStorage when perfect-hash feature is enabled
123#[cfg(feature = "perfect-hash")]
124pub use storage::FrozenStorage;
125
126pub use container::*;
127pub use error::*;
128pub use factory::*;
129pub use provider::*;
130pub use scope::*;
131
132// Re-export tracing macros for convenience when logging feature is enabled
133#[cfg(feature = "logging")]
134pub use tracing::{debug, error, info, trace, warn};
135
136// Re-export derive macros when feature is enabled
137#[cfg(feature = "derive")]
138pub use dependency_injector_derive::{Inject, Service, TypedRequire};
139
140// Re-export for convenience
141pub use std::sync::Arc;
142
143/// Prelude for convenient imports
144pub mod prelude {
145 pub use crate::{
146 BatchBuilder, BatchRegistrar, Container, DiError, Factory, Injectable, Lifetime,
147 PooledScope, Provider, Result, Scope, ScopePool, ScopedContainer,
148 };
149 pub use std::sync::Arc;
150
151 // Compile-time safety types
152 pub use crate::typed::{
153 DeclaresDeps, DepsPresent, Has, HasService, HasType, Reg, TypedBuilder, TypedContainer,
154 };
155 pub use crate::verified::{Resolvable, Service, ServiceModule, ServiceProvider};
156
157 #[cfg(feature = "async")]
158 pub use crate::async_support::AsyncLazy;
159
160 #[cfg(feature = "derive")]
161 pub use crate::{Inject, Service as ServiceDerive, TypedRequire};
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use std::sync::atomic::{AtomicU32, Ordering};
168
169 #[derive(Clone)]
170 struct Database {
171 url: String,
172 }
173
174 #[allow(dead_code)]
175 #[derive(Clone)]
176 struct UserService {
177 name: String,
178 }
179
180 #[test]
181 fn test_singleton_registration() {
182 let container = Container::new();
183 container.singleton(Database { url: "test".into() });
184
185 let db = container.get::<Database>().unwrap();
186 assert_eq!(db.url, "test");
187 }
188
189 #[test]
190 fn test_multiple_resolve_same_instance() {
191 let container = Container::new();
192 container.singleton(Database { url: "test".into() });
193
194 let db1 = container.get::<Database>().unwrap();
195 let db2 = container.get::<Database>().unwrap();
196
197 // Same Arc instance
198 assert!(Arc::ptr_eq(&db1, &db2));
199 }
200
201 #[test]
202 fn test_transient_creates_new_instance() {
203 static COUNTER: AtomicU32 = AtomicU32::new(0);
204
205 #[derive(Clone)]
206 struct Counter(u32);
207
208 let container = Container::new();
209 container.transient(|| Counter(COUNTER.fetch_add(1, Ordering::SeqCst)));
210
211 let c1 = container.get::<Counter>().unwrap();
212 let c2 = container.get::<Counter>().unwrap();
213
214 assert_ne!(c1.0, c2.0);
215 }
216
217 #[test]
218 fn test_lazy_singleton() {
219 static CREATED: AtomicU32 = AtomicU32::new(0);
220
221 #[derive(Clone)]
222 struct LazyService;
223
224 let container = Container::new();
225 container.lazy(|| {
226 CREATED.fetch_add(1, Ordering::SeqCst);
227 LazyService
228 });
229
230 assert_eq!(CREATED.load(Ordering::SeqCst), 0);
231
232 let _ = container.get::<LazyService>().unwrap();
233 assert_eq!(CREATED.load(Ordering::SeqCst), 1);
234
235 // Second resolve doesn't create new instance
236 let _ = container.get::<LazyService>().unwrap();
237 assert_eq!(CREATED.load(Ordering::SeqCst), 1);
238 }
239
240 #[test]
241 fn test_scoped_container() {
242 let root = Container::new();
243 root.singleton(Database { url: "root".into() });
244
245 let child = root.scope();
246 child.singleton(UserService {
247 name: "child".into(),
248 });
249
250 // Child can access root services
251 assert!(child.contains::<Database>());
252 assert!(child.contains::<UserService>());
253
254 // Root cannot access child services
255 assert!(root.contains::<Database>());
256 assert!(!root.contains::<UserService>());
257 }
258
259 #[test]
260 fn test_not_found_error() {
261 let container = Container::new();
262 let result = container.get::<Database>();
263 assert!(result.is_err());
264 }
265
266 #[test]
267 fn test_override_in_scope() {
268 let root = Container::new();
269 root.singleton(Database {
270 url: "production".into(),
271 });
272
273 let test_scope = root.scope();
274 test_scope.singleton(Database { url: "test".into() });
275
276 // Root has production
277 let root_db = root.get::<Database>().unwrap();
278 assert_eq!(root_db.url, "production");
279
280 // Child has test override
281 let child_db = test_scope.get::<Database>().unwrap();
282 assert_eq!(child_db.url, "test");
283 }
284}