Skip to main content

injectable_rs_runtime/
inject.rs

1//! The `Inject<T>` wrapper — the primary extraction type for dependencies.
2//!
3//! `Inject<T>` wraps `Arc<T>` and is the type that appears in constructor
4//! parameter lists. It implements [`Extract`](crate::Extract) by delegating
5//! to `T::Provider::provide(ctx)`.
6
7use std::sync::Arc;
8
9use crate::{Extract, InjectableResult, Provider, ResolveContext};
10
11/// A wrapper around `Arc<T>` that can be extracted from a [`ResolveContext`].
12///
13/// `T` may be unsized (`dyn Trait`) for trait-object injection set up with
14/// `bind!(dyn Trait => Concrete)`.  For sized concrete types the standard
15/// `Extract` impl applies; for `dyn Trait` the generated provider code uses
16/// `ctx.resolve_external::<Arc<dyn Trait>>()` directly (no `Extract` impl
17/// needed — and no orphan-rule violation).
18///
19/// # Examples
20///
21/// ```rust,ignore
22/// // Concrete injectable type
23/// pub struct UserService { db: Inject<Database> }
24///
25/// // Trait-object injection (set up with bind!)
26/// pub struct NotificationService { mailer: Inject<dyn Mailer> }
27///
28/// // Axum handler destructuring pattern
29/// async fn handler(Inject(svc): Inject<UserService>) -> impl IntoResponse { ... }
30/// ```
31pub struct Inject<T: ?Sized>(pub Arc<T>);
32
33// ── Clone ─────────────────────────────────────────────────────────────────
34// Manual impl so that the bound is `T: ?Sized` (derive adds `T: Clone`).
35// Cloning an `Inject<T>` just increments the Arc refcount; it does NOT
36// require `T: Clone`.
37impl<T: ?Sized> Clone for Inject<T> {
38    fn clone(&self) -> Self {
39        Inject(Arc::clone(&self.0))
40    }
41}
42
43// ── Debug ─────────────────────────────────────────────────────────────────
44// Manual impl so that the bound is `T: ?Sized + Debug` (derive adds `T: Debug`
45// without the `?Sized` relaxation, which prevents `Inject<dyn Trait>: Debug`).
46impl<T: ?Sized + std::fmt::Debug> std::fmt::Debug for Inject<T> {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        f.debug_tuple("Inject").field(&&*self.0).finish()
49    }
50}
51
52// ── Core methods ──────────────────────────────────────────────────────────
53
54impl<T: ?Sized> Inject<T> {
55    /// Create a new `Inject` from an `Arc<T>`.
56    pub fn new(value: Arc<T>) -> Self {
57        Self(value)
58    }
59
60    /// Consume the wrapper and return the inner `Arc<T>`.
61    pub fn into_inner(self) -> Arc<T> {
62        self.0
63    }
64
65    /// Borrow the inner `Arc<T>`.
66    pub fn inner(&self) -> &Arc<T> {
67        &self.0
68    }
69
70    /// Clone the inner `Arc<T>`.
71    pub fn arc(&self) -> Arc<T> {
72        Arc::clone(&self.0)
73    }
74
75    /// Returns `true` if both `Inject<T>` values point to the same heap allocation.
76    ///
77    /// Useful for asserting singleton semantics in tests without going through
78    /// `Arc::ptr_eq(a.inner(), b.inner())`.
79    pub fn ptr_eq(&self, other: &Inject<T>) -> bool {
80        Arc::ptr_eq(&self.0, &other.0)
81    }
82}
83
84// ── Deref ─────────────────────────────────────────────────────────────────
85
86impl<T: ?Sized> std::ops::Deref for Inject<T> {
87    type Target = T;
88
89    fn deref(&self) -> &Self::Target {
90        &self.0
91    }
92}
93
94// ── Conversions ───────────────────────────────────────────────────────────
95
96impl<T: ?Sized> From<Arc<T>> for Inject<T> {
97    fn from(arc: Arc<T>) -> Self {
98        Self(arc)
99    }
100}
101
102impl<T: ?Sized> From<Inject<T>> for Arc<T> {
103    fn from(inject: Inject<T>) -> Self {
104        inject.into_inner()
105    }
106}
107
108// ── Value-based comparison (requires T: Sized for deref comparisons) ──────
109
110impl<T: PartialEq> PartialEq for Inject<T> {
111    fn eq(&self, other: &Self) -> bool {
112        **self == **other
113    }
114}
115
116impl<T: Eq> Eq for Inject<T> {}
117
118impl<T: std::hash::Hash> std::hash::Hash for Inject<T> {
119    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
120        (**self).hash(state);
121    }
122}
123
124// ── AsRef / Borrow ────────────────────────────────────────────────────────
125
126impl<T: ?Sized> AsRef<T> for Inject<T> {
127    fn as_ref(&self) -> &T {
128        self
129    }
130}
131
132impl<T: ?Sized> std::borrow::Borrow<T> for Inject<T> {
133    fn borrow(&self) -> &T {
134        self
135    }
136}
137
138// ── Extract ───────────────────────────────────────────────────────────────
139//
140// Only implemented for `T: Sized`.  For `Inject<dyn Trait>` the generated
141// provider code calls `ctx.resolve_external::<Arc<dyn Trait>>()` directly
142// (keyed by the `InjectableArcFactory` entry submitted by `bind!`), avoiding
143// both the `T: Sized` requirement and the orphan-rule violation that would
144// arise from `impl Extract for Inject<dyn UserTrait>` in user crates.
145
146/// `Extract` implementation for `Inject<T>` where `T: Sized + Send + Sync + 'static`.
147///
148/// Resolution order:
149/// 1. Try `resolve_external::<Arc<T>>()` — finds `InjectableArcFactory` entries
150///    submitted by `#[injectable]` macros.
151///    These entries call `resolve_singleton_arc` internally, so singletons are
152///    properly cached.
153/// 2. Fall back to `resolve_external::<T>()` — finds `DynProvider<T>` registrations
154///    for external types, then wraps the result in `Arc::new`.
155#[async_trait::async_trait]
156impl<T: Sized + Send + Sync + 'static> Extract for Inject<T> {
157    async fn extract(ctx: &ResolveContext) -> InjectableResult<Self> {
158        // Path 1: Injectable types via InjectableArcFactory (keyed by Arc<T>).
159        if let Some(result) = ctx.try_resolve_external::<Arc<T>>().await {
160            return result.map(Inject);
161        }
162        // Path 2: External types registered via DynProvider<T>.
163        ctx.resolve_external::<T>()
164            .await
165            .map(|t| Inject(Arc::new(t)))
166    }
167}
168
169/// `Extract` for `Arc<T>` where `T: Injectable`.
170///
171/// Defined inside `injectable_rs_runtime` (where `Extract` is local) so the orphan
172/// rule is satisfied. This replaces the previous special-case codegen for
173/// `Arc<T>` fields — the `Extract` impl lives in one place and any
174/// `Arc<WeatherService>` field just works without annotation.
175///
176/// Singletons: returns the cached `Arc` (same pointer every call).
177/// Transients: wraps a fresh instance in `Arc::new`.
178#[async_trait::async_trait]
179impl<T: crate::Injectable> Extract for Arc<T> {
180    async fn extract(ctx: &ResolveContext) -> InjectableResult<Self> {
181        if T::IS_SINGLETON {
182            ctx.resolve_singleton_arc::<T>().await
183        } else {
184            let v = T::Provider::provide(ctx).await?;
185            Ok(Arc::new(v))
186        }
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193    use std::collections::hash_map::DefaultHasher;
194    use std::hash::{Hash, Hasher};
195
196    fn make_inject(v: u32) -> Inject<u32> {
197        Inject::new(Arc::new(v))
198    }
199
200    #[test]
201    fn from_inject_into_arc() {
202        let inj = make_inject(42);
203        let arc: Arc<u32> = inj.into();
204        assert_eq!(*arc, 42);
205    }
206
207    #[test]
208    fn from_arc_into_inject() {
209        let arc = Arc::new(99u32);
210        let inj: Inject<u32> = arc.into();
211        assert_eq!(*inj, 99);
212    }
213
214    #[test]
215    fn partial_eq_same_value() {
216        let a = make_inject(1);
217        let b = make_inject(1);
218        assert_eq!(a, b);
219    }
220
221    #[test]
222    fn partial_eq_different_value() {
223        let a = make_inject(1);
224        let b = make_inject(2);
225        assert_ne!(a, b);
226    }
227
228    #[test]
229    fn hash_equals_inner_hash() {
230        let inj = make_inject(77);
231        let mut h1 = DefaultHasher::new();
232        inj.hash(&mut h1);
233        let mut h2 = DefaultHasher::new();
234        77u32.hash(&mut h2);
235        assert_eq!(h1.finish(), h2.finish());
236    }
237
238    #[test]
239    fn as_ref() {
240        let inj = make_inject(5);
241        let r: &u32 = inj.as_ref();
242        assert_eq!(*r, 5);
243    }
244
245    #[test]
246    fn borrow() {
247        use std::borrow::Borrow;
248        let inj = make_inject(10);
249        let b: &u32 = inj.borrow();
250        assert_eq!(*b, 10);
251    }
252
253    #[test]
254    fn debug_contains_inject() {
255        let inj = make_inject(7);
256        let s = format!("{inj:?}");
257        assert!(s.contains("Inject"));
258        assert!(s.contains('7'));
259    }
260
261    #[test]
262    fn clone_shares_arc() {
263        let inj = make_inject(3);
264        let cloned = inj.clone();
265        assert!(Arc::ptr_eq(&inj.0, &cloned.0));
266    }
267
268    #[test]
269    fn dyn_trait_inject_new() {
270        let arc: Arc<dyn std::fmt::Debug> = Arc::new(42u32);
271        let inj: Inject<dyn std::fmt::Debug> = Inject::new(arc);
272        let s = format!("{:?}", &*inj);
273        assert!(s.contains("42"));
274    }
275}