reinhardt_di/injectable.rs
1//! Injectable trait for dependencies
2
3use crate::{DiResult, InjectableKey, context::InjectionContext};
4
5/// Injectable trait for dependencies.
6///
7/// This trait defines how a type can be injected as a dependency.
8/// Types implementing this trait can be used as direct `#[inject]` parameters.
9///
10/// # Blanket Implementations
11///
12/// The following blanket implementations are provided:
13///
14/// - **`Arc<T>`** where `T: Injectable` — injects the inner `T` and wraps it in `Arc`
15/// - **`Depends<K, T>`** — resolves `FactoryOutput<K, T>` via the global registry
16/// - **`Option<T>`** where `T: Injectable` — returns `None` on injection failure
17/// instead of propagating the error
18///
19/// # Custom Implementation
20///
21/// To make a type injectable, use one of these approaches:
22///
23/// 1. **`#[injectable]` attribute macro** — generates an `Injectable` impl from
24/// a keyed provider function returning `FactoryOutput<K, T>`
25/// 2. **`#[injectable_factory]` attribute macro** — deprecated compatibility
26/// alias for `#[injectable]` provider functions
27/// 3. **Manual `impl Injectable`** — implement the trait directly with
28/// `#[async_trait]`
29///
30/// # Example
31///
32/// ```rust,no_run
33/// use reinhardt_di::{DiResult, Injectable, InjectionContext};
34/// use async_trait::async_trait;
35///
36/// # #[derive(Clone)]
37/// # struct DbPool;
38/// # impl DbPool {
39/// # async fn connect() -> DiResult<Self> { Ok(DbPool) }
40/// # }
41/// struct Database {
42/// pool: DbPool,
43/// }
44///
45/// #[async_trait]
46/// impl Injectable for Database {
47/// async fn inject(_ctx: &InjectionContext) -> DiResult<Self> {
48/// Ok(Database {
49/// pool: DbPool::connect().await?,
50/// })
51/// }
52/// }
53/// ```
54#[async_trait::async_trait]
55pub trait Injectable: Sized + Send + Sync + 'static {
56 /// Creates an instance of this type by resolving dependencies from the injection context.
57 async fn inject(ctx: &InjectionContext) -> DiResult<Self>;
58
59 /// Inject without using cache (for `cache = false` support).
60 ///
61 /// This method creates a new instance without checking or updating any cache.
62 /// By default, it delegates to `inject()`, but types can override this
63 /// to provide cache-free injection.
64 async fn inject_uncached(ctx: &InjectionContext) -> DiResult<Self> {
65 Self::inject(ctx).await
66 }
67}
68
69/// Blanket implementation of Injectable for `Arc<T>`
70///
71/// This allows using `Arc<T>` directly in endpoint handlers with `#[inject]`:
72///
73/// ```ignore
74/// # use reinhardt_di::Injectable;
75/// # use std::sync::Arc;
76/// # struct DatabaseConnection;
77/// # struct Response;
78/// # type ViewResult<T> = Result<T, Box<dyn std::error::Error>>;
79/// # use reinhardt_core::endpoint;
80/// #[endpoint]
81/// async fn handler(
82/// #[inject] db: Arc<DatabaseConnection>,
83/// ) -> ViewResult<Response> {
84/// // ...
85/// # Ok(Response)
86/// }
87/// ```
88///
89/// The implementation injects `T` first, then wraps it in `Arc`.
90#[async_trait::async_trait]
91impl<T> Injectable for std::sync::Arc<T>
92where
93 T: Injectable,
94{
95 async fn inject(ctx: &InjectionContext) -> DiResult<Self> {
96 T::inject(ctx).await.map(std::sync::Arc::new)
97 }
98
99 async fn inject_uncached(ctx: &InjectionContext) -> DiResult<Self> {
100 T::inject_uncached(ctx).await.map(std::sync::Arc::new)
101 }
102}
103
104/// Blanket implementation of Injectable for `Depends<K, T>`
105///
106/// This allows using `Depends<K, T>` directly in endpoint handlers with `#[inject]`:
107///
108/// ```ignore
109/// # use reinhardt_di::{Depends, Injectable, InjectableKey};
110/// # struct DatabaseConnectionKey;
111/// # impl InjectableKey for DatabaseConnectionKey {}
112/// # struct DatabaseConnection;
113/// # struct Response;
114/// # type ViewResult<T> = Result<T, Box<dyn std::error::Error>>;
115/// # use reinhardt_core::endpoint;
116/// #[endpoint]
117/// async fn handler(
118/// #[inject] db: Depends<DatabaseConnectionKey, DatabaseConnection>,
119/// ) -> ViewResult<Response> {
120/// // ...
121/// # Ok(Response)
122/// }
123/// ```
124///
125/// The implementation delegates to `Depends::resolve_from_registry()`, which
126/// resolves `FactoryOutput<K, T>` from the global registry.
127#[async_trait::async_trait]
128impl<K, T> Injectable for crate::depends::Depends<K, T>
129where
130 K: InjectableKey,
131 T: Send + Sync + 'static,
132{
133 async fn inject(ctx: &InjectionContext) -> DiResult<Self> {
134 crate::depends::Depends::<K, T>::resolve_from_registry(ctx, true).await
135 }
136
137 async fn inject_uncached(ctx: &InjectionContext) -> DiResult<Self> {
138 crate::depends::Depends::<K, T>::resolve_from_registry(ctx, false).await
139 }
140}
141
142/// Blanket implementation of Injectable for `Option<T>`
143///
144/// This allows optional injection where failure results in `None`
145/// instead of an error. Useful for endpoints that serve both
146/// authenticated and anonymous users.
147///
148/// # Security Note
149///
150/// `Option<T>` swallows ALL injection errors into `None`.
151/// For security-critical endpoints, use `T` directly to ensure
152/// errors are surfaced as HTTP 401/500.
153///
154/// ```ignore
155/// # use reinhardt_di::Injectable;
156/// # struct AuthInfo;
157/// # struct Response;
158/// # type ViewResult<T> = Result<T, Box<dyn std::error::Error>>;
159/// # use reinhardt_core::endpoint;
160/// #[endpoint]
161/// async fn handler(
162/// #[inject] auth: Option<AuthInfo>,
163/// ) -> ViewResult<Response> {
164/// // auth is None if not authenticated
165/// # Ok(Response)
166/// }
167/// ```
168#[async_trait::async_trait]
169impl<T> Injectable for Option<T>
170where
171 T: Injectable,
172{
173 async fn inject(ctx: &InjectionContext) -> DiResult<Self> {
174 match T::inject(ctx).await {
175 Ok(value) => Ok(Some(value)),
176 Err(_) => Ok(None),
177 }
178 }
179
180 async fn inject_uncached(ctx: &InjectionContext) -> DiResult<Self> {
181 match T::inject_uncached(ctx).await {
182 Ok(value) => Ok(Some(value)),
183 Err(_) => Ok(None),
184 }
185 }
186}