Skip to main content

reinhardt_di/
injectable.rs

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