Skip to main content

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}