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/// # Automatic Implementation
11///
12/// Types that implement `Default + Clone + Send + Sync + 'static` automatically
13/// get an `Injectable` implementation that:
14/// 1. Checks if the value is already cached in the request scope
15/// 2. Checks if the value is available in the singleton scope
16/// 3. Creates a new instance using `Default::default()`
17///
18/// This automatic behavior is similar to FastAPI's dependency injection,
19/// where simple types can be auto-injected without explicit implementation.
20///
21/// # Example
22///
23/// ```rust,no_run
24/// use reinhardt_di::{Injectable, InjectionContext, DiResult, Depends};
25/// use async_trait::async_trait;
26///
27// Automatic injection for types with Default + Clone
28/// #[derive(Default, Clone)]
29/// struct Config {
30/// api_key: String,
31/// }
32///
33// Config now has Injectable automatically
34// Can be used directly: Depends<Config>
35///
36// Custom injection logic
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/// // Custom logic here
50/// Ok(Database {
51/// pool: DbPool::connect().await?,
52/// })
53/// }
54/// }
55/// ```
56#[async_trait::async_trait]
57pub trait Injectable: Sized + Send + Sync + 'static {
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 + Clone,
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}