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}