rust_dix/lib.rs
1//! LRDI — Rust Dependency Injection Framework
2//!
3//! A dependency injection container for Rust inspired by
4//! Microsoft.Extensions.DependencyInjection (MEDI).
5//!
6//! # Quick start
7//!
8//! ```rust
9//! use rust_dix::*;
10//! use std::sync::Arc;
11//!
12//! // Trait-based interface (recommended)
13//! trait ILogger: Send + Sync {
14//! fn log(&self, msg: &str);
15//! }
16//!
17//! struct ConsoleLogger;
18//! impl ILogger for ConsoleLogger {
19//! fn log(&self, msg: &str) { println!("[LOG] {}", msg); }
20//! }
21//!
22//! // Register as trait interface
23//! let provider = ServiceCollection::new()
24//! .singleton::<dyn ILogger>(|_| Arc::new(ConsoleLogger))
25//! .build()
26//! .unwrap();
27//!
28//! // Resolve as trait
29//! let logger: Arc<dyn ILogger> = provider.get().unwrap();
30//! logger.log("Hello");
31//! ```
32//!
33//! # Keyed services (strategy pattern)
34//!
35//! ```rust
36//! use rust_dix::*;
37//! use std::sync::Arc;
38//!
39//! trait Strategy: Send + Sync { fn execute(&self) -> &'static str; }
40//!
41//! struct FastStrategy;
42//! impl Strategy for FastStrategy { fn execute(&self) -> &'static str { "fast" } }
43//!
44//! struct SafeStrategy;
45//! impl Strategy for SafeStrategy { fn execute(&self) -> &'static str { "safe" } }
46//!
47//! let provider = ServiceCollection::new()
48//! .keyed_singleton::<dyn Strategy>("fast", |_| Arc::new(FastStrategy))
49//! .keyed_singleton::<dyn Strategy>("safe", |_| Arc::new(SafeStrategy))
50//! .build()
51//! .unwrap();
52//!
53//! let fast: Arc<dyn Strategy> = provider.get_keyed("fast").unwrap();
54//! assert_eq!(fast.execute(), "fast");
55//! ```
56//!
57//! # Build-time validation
58//!
59//! LRDI validates dependencies during `build()` to catch errors early:
60//!
61//! ```rust
62//! # use rust_dix::*;
63//! # use std::sync::Arc;
64//! # struct A;
65//! # struct B;
66//! // Circular dependency detection — factories resolve deps via `get_any`
67//! // (the `get::<T>()` method requires `Self: Sized` and cannot be called
68//! // on `&dyn IServiceResolver`).
69//! let result = ServiceCollection::new()
70//! .singleton(|r| {
71//! let _b = r.get_any(std::any::type_name::<B>()); // A depends on B
72//! Arc::new(A)
73//! })
74//! .singleton(|r| {
75//! let _a = r.get_any(std::any::type_name::<A>()); // B depends on A → CIRCULAR!
76//! Arc::new(B)
77//! })
78//! .build();
79//!
80//! assert!(result.is_err());
81//! if let Err(RdiError::CircularDependency(cycle)) = result {
82//! println!("Circular dependency detected: {}", cycle);
83//! }
84//! ```
85//!
86//! # Async support
87//!
88//! For services that require async initialization (e.g., database
89//! connections, remote config loading), use `build_async()` and
90//! the `async_*` registration methods. `build_async()` returns
91//! `Arc<ServiceProvider>` so that `provider_arc()` is available for
92//! async resolution:
93//!
94//! ```rust,ignore
95//! use rust_dix::*;
96//! use std::sync::Arc;
97//!
98//! struct DbPool { conn: String }
99//!
100//! async fn connect_to_db() -> DbPool {
101//! // async connection setup...
102//! DbPool { conn: "connected".into() }
103//! }
104//!
105//! # async fn run() {
106//! let provider = ServiceCollection::new()
107//! .async_singleton(|_| Box::pin(async {
108//! Arc::new(connect_to_db().await)
109//! }))
110//! .build_async()
111//! .await
112//! .unwrap();
113//!
114//! let pool: Arc<DbPool> = provider.get_async().await.unwrap();
115//! # }
116//! ```
117//!
118//! # Features
119//!
120//! - **Three lifetimes**: Singleton, Scoped, Transient
121//! - **Keyed services**: multiple implementations of the same trait by key
122//! - `keyed_singleton(key, factory)` — shared globally
123//! - `keyed_scoped(key, factory)` — shared within scope
124//! - `keyed_transient(key, factory)` — new instance each time
125//! - **Constructor injection**: `#[derive(Inject)]` on structs
126//! - **Attribute-based auto-registration**: `#[rust_dix::inject]` on structs or trait impls
127//! - **Compile-time module scanning**: `#[rust_dix::module]` collects `register!()` declarations
128//! - **Cross-DLL support**: named service registry for cdylib plugins
129//! - **Layered containers**: child-first resolution via `ServiceProviderWrapper`
130//! - **Build-time validation**: circular dependency detection during `build()`
131//!
132//! # Crate layout
133//!
134//! | Module | Description |
135//! |--------|-------------|
136//! | [`ServiceCollection`] | Register services with a builder API |
137//! | [`ServiceProvider`] | The root DI container |
138//! | [`Scope`] / [`ServiceScope`] | Scoped container (one per scope) |
139//! | [`IServiceResolver`] | Resolution trait (implemented by `ServiceProvider` & `Scope`) |
140//! | [`ServiceProviderWrapper`] | Child-first layered container |
141//! | [`ServiceLifetime`] | Enum: Singleton, Scoped, Transient |
142//! | [`IProvider`] | Unified trait for resolution + named registry |
143//! | [`RdiError`] | Error types (includes `CircularDependency`) |
144//!
145//! # Proc-macros
146//!
147//! - `#[derive(Inject)]` — auto-generates constructor injection code
148//! - `#[rust_dix::inject]` — auto-registration on structs or trait impls
149//! - `#[rust_dix::module]` — compile-time module scanning
150//! - `rust_dix::register!(...)` — declare services inside `#[rust_dix::module]`
151
152pub mod bridge;
153mod cache;
154pub mod collection;
155pub mod entry;
156pub mod error;
157pub mod provider;
158pub mod registration;
159pub mod scope;
160mod validation;
161pub mod wrapper;
162
163// ── Core types (MEDI-inspired naming) ──
164
165/// Service collection — register services, then call `.build()`.
166///
167/// Analogous to `IServiceCollection` in Microsoft.Extensions.DependencyInjection.
168pub use collection::ServiceCollection;
169
170/// Built service provider — the root DI container (read-only after build).
171///
172/// 兼具 MEDI 中 `IServiceProvider` 与 root scope 双重身份:
173/// - 作为 `IServiceProvider`:提供类型/键控/命名解析入口。
174/// - 作为 root scope:从根直接解析 Scoped 服务时,实例在根内缓存复用,
175/// 语义等同于在根 scope 内单例化。通过 [`ServiceProvider::scope`] 创建的子 Scope
176/// 拥有独立的 scoped 缓存,不回退到根。
177///
178/// Analogous to `IServiceProvider` in MEDI.
179pub use provider::ServiceProvider;
180
181/// Service descriptor containing registration metadata.
182pub use entry::ServiceDescriptor;
183
184/// Service lifetime enum: [`Singleton`](ServiceLifetime::Singleton),
185/// [`Scoped`](ServiceLifetime::Scoped), [`Transient`](ServiceLifetime::Transient).
186pub use entry::ServiceLifetime;
187
188/// Core resolution trait. Both [`ServiceProvider`] and [`Scope`] implement this.
189pub use entry::IServiceResolver;
190
191/// A scoped service provider — created via [`ServiceProvider::scope`].
192///
193/// Analogous to `IServiceScope` in MEDI.
194pub use scope::Scope;
195
196/// Alias for [`Scope`] with MEDI-inspired naming (`IServiceScope`).
197pub use scope::Scope as ServiceScope;
198
199/// Factory trait for creating independent scopes. Injectable into singleton
200/// services that need on-demand scope creation (e.g., background workers).
201pub use scope::ScopeFactory;
202
203/// Internal types used by the container.
204pub use entry::{ServiceEntry, ServiceFactory};
205
206/// Wrapper combining child + root provider with child-first resolution.
207pub use wrapper::ServiceProviderWrapper;
208
209// ── Plugin / cross-DLL support ──
210
211/// Unified trait for service resolution and named registry access.
212pub use bridge::IProvider;
213
214// ── Error types ──
215
216/// Error types returned from service resolution.
217pub use error::RdiError;
218
219// ── Runtime registration ──
220
221pub use registration::ServiceRegistration;
222
223/// Re-export of `inventory` so that `#[rust_dix::inject]` generated code
224/// can call `rust_dix::inventory::submit!` without requiring downstream crates
225/// to add `inventory` to their own `Cargo.toml`.
226#[doc(hidden)]
227pub use inventory;
228
229// ── Proc-macros ──
230
231// `inject` is the attribute macro (placed on structs or trait impls).
232// `register` is the function-like macro used inside `#[rust_dix::module]`.
233pub use rust_dix_macros::{inject, module, register, Inject};