1use std::any::{Any, TypeId};
36use std::collections::HashMap;
37use std::sync::Arc;
38
39use crate::{DynProvider, InjectableError, InjectableResult, ResolveContext};
40
41pub const DEFAULT_TOKEN: &str = "";
54
55pub type ErasedProviderPinnedFuture<'a> = std::pin::Pin<
56 Box<dyn std::future::Future<Output = InjectableResult<Box<dyn Any + Send>>> + Send + 'a>,
57>;
58
59trait ErasedProvider: Send + Sync + 'static {
61 fn provide_as_any(&self, ctx: Arc<ResolveContext>) -> ErasedProviderPinnedFuture<'_>;
62}
63
64impl<T: Send + Sync + 'static> ErasedProvider for DynProvider<T> {
65 fn provide_as_any(&self, ctx: Arc<ResolveContext>) -> ErasedProviderPinnedFuture<'_> {
66 Box::pin(async move {
67 let value = self.provide(ctx).await?;
68 Ok(Box::new(value) as Box<dyn Any + Send>)
69 })
70 }
71}
72
73type RegistryKey = (TypeId, String);
79
80pub struct ProviderRegistry {
99 providers: HashMap<RegistryKey, Box<dyn ErasedProvider>>,
100 duplicates: Vec<String>,
103}
104
105impl ProviderRegistry {
106 pub fn new() -> Self {
108 Self {
109 providers: HashMap::new(),
110 duplicates: Vec::new(),
111 }
112 }
113
114 fn make_key<T: 'static>(token: &str) -> RegistryKey {
115 (TypeId::of::<T>(), token.to_string())
116 }
117
118 fn duplicate_label<T: 'static>(token: &str) -> String {
119 if token.is_empty() {
120 std::any::type_name::<T>().to_string()
121 } else {
122 format!("{}[{}]", std::any::type_name::<T>(), token)
123 }
124 }
125
126 pub fn register<T: Send + Sync + 'static>(
150 &mut self,
151 token: impl Into<String>,
152 provider: DynProvider<T>,
153 ) {
154 let token = token.into();
155 let key = Self::make_key::<T>(&token);
156 if self.providers.contains_key(&key) {
157 self.duplicates.push(Self::duplicate_label::<T>(&token));
158 }
159 self.providers.insert(key, Box::new(provider));
160 }
161
162 pub fn register_or_replace<T: Send + Sync + 'static>(
169 &mut self,
170 token: impl Into<String>,
171 provider: DynProvider<T>,
172 ) {
173 let key = (TypeId::of::<T>(), token.into());
174 self.providers.insert(key, Box::new(provider));
175 }
176
177 pub fn duplicates(&self) -> &[String] {
179 &self.duplicates
180 }
181
182 pub fn has<T: 'static>(&self) -> bool {
186 self.has_with_token::<T>(DEFAULT_TOKEN)
187 }
188
189 pub fn has_with_token<T: 'static>(&self, token: &str) -> bool {
191 self.providers.contains_key(&Self::make_key::<T>(token))
192 }
193
194 pub(crate) async fn resolve_with_token<T: Send + Sync + 'static>(
203 &self,
204 token: &str,
205 ctx: Arc<ResolveContext>,
206 ) -> Option<InjectableResult<T>> {
207 let key = Self::make_key::<T>(token);
208
209 if let Some(provider) = self.providers.get(&key) {
211 let result = provider.provide_as_any(Arc::clone(&ctx)).await;
212 return Some(
213 result.and_then(|boxed| match boxed.downcast::<T>() {
214 Ok(t) => Ok(*t),
215 Err(_) => Err(InjectableError::ConstructionFailed {
216 type_name: std::any::type_name::<T>(),
217 reason: "downcast failed (this should never happen with correct TypeId)"
218 .to_string(),
219 }),
220 }),
221 );
222 }
223
224 if token == DEFAULT_TOKEN {
227 let target_id = TypeId::of::<T>();
228 for factory in inventory::iter::<InjectableArcFactory>() {
229 if factory.type_id() == target_id {
230 let result = factory.provide(ctx).await;
231 return Some(result.and_then(|boxed| match boxed.downcast::<T>() {
232 Ok(t) => Ok(*t),
233 Err(_) => Err(InjectableError::ConstructionFailed {
234 type_name: std::any::type_name::<T>(),
235 reason: "InjectableArcFactory downcast failed".to_string(),
236 }),
237 }));
238 }
239 }
240 }
241
242 None
243 }
244
245 pub fn len(&self) -> usize {
247 self.providers.len()
248 }
249
250 pub fn is_empty(&self) -> bool {
252 self.providers.is_empty()
253 }
254}
255
256impl Default for ProviderRegistry {
257 fn default() -> Self {
258 Self::new()
259 }
260}
261
262impl std::fmt::Debug for ProviderRegistry {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 f.debug_struct("ProviderRegistry")
265 .field("count", &self.providers.len())
266 .finish()
267 }
268}
269
270pub type InjectableProvideFnPtr = fn(
273 std::sync::Arc<ResolveContext>,
274) -> std::pin::Pin<
275 Box<
276 dyn std::future::Future<Output = InjectableResult<Box<dyn std::any::Any + Send>>>
277 + Send
278 + 'static,
279 >,
280>;
281
282pub struct InjectableArcFactory {
286 pub type_name: &'static str,
288 type_id_fn: fn() -> std::any::TypeId,
289 provide_fn: InjectableProvideFnPtr,
290}
291
292impl InjectableArcFactory {
293 pub const fn new_const(
295 type_name: &'static str,
296 type_id_fn: fn() -> std::any::TypeId,
297 provide_fn: InjectableProvideFnPtr,
298 ) -> Self {
299 Self {
300 type_name,
301 type_id_fn,
302 provide_fn,
303 }
304 }
305
306 pub fn type_id(&self) -> std::any::TypeId {
308 (self.type_id_fn)()
309 }
310
311 pub fn provide(&self, ctx: std::sync::Arc<ResolveContext>) -> ErasedProviderPinnedFuture<'_> {
313 (self.provide_fn)(ctx)
314 }
315}
316
317inventory::collect!(InjectableArcFactory);
318
319pub type PostConstructFnPtr = fn(
324 std::sync::Arc<dyn std::any::Any + std::marker::Send + std::marker::Sync>,
325) -> std::pin::Pin<
326 Box<dyn std::future::Future<Output = crate::HookResult> + std::marker::Send + 'static>,
327>;
328
329pub type MakePreDestructFnPtr = fn(
332 std::sync::Arc<dyn std::any::Any + std::marker::Send + std::marker::Sync>,
333) -> std::sync::Arc<dyn crate::PreDestruct>;
334
335pub struct InjectableHooksEntry {
338 type_id_fn: fn() -> std::any::TypeId,
339 post_construct_fn: Option<PostConstructFnPtr>,
340 make_pre_destruct_fn: Option<MakePreDestructFnPtr>,
341}
342
343impl InjectableHooksEntry {
344 pub const fn new_const(
346 type_id_fn: fn() -> std::any::TypeId,
347 post_construct_fn: Option<PostConstructFnPtr>,
348 make_pre_destruct_fn: Option<MakePreDestructFnPtr>,
349 ) -> Self {
350 Self {
351 type_id_fn,
352 post_construct_fn,
353 make_pre_destruct_fn,
354 }
355 }
356
357 pub fn type_id(&self) -> std::any::TypeId {
359 (self.type_id_fn)()
360 }
361
362 pub fn post_construct_fn(&self) -> Option<PostConstructFnPtr> {
364 self.post_construct_fn
365 }
366
367 pub fn make_pre_destruct_fn(&self) -> Option<MakePreDestructFnPtr> {
369 self.make_pre_destruct_fn
370 }
371}
372
373inventory::collect!(InjectableHooksEntry);
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use crate::DynProvider;
379
380 #[test]
381 fn new_registry_is_empty() {
382 let r = ProviderRegistry::new();
383 assert!(r.is_empty());
384 assert_eq!(r.len(), 0);
385 }
386
387 #[test]
388 fn has_returns_false_for_unregistered() {
389 let r = ProviderRegistry::new();
390 assert!(!r.has::<u32>());
391 assert!(!r.has_with_token::<u32>("primary"));
392 }
393
394 #[test]
395 fn has_returns_true_after_register_default() {
396 let mut r = ProviderRegistry::new();
397 r.register("", DynProvider::from_value(42u32));
398 assert!(r.has::<u32>());
399 assert!(r.has_with_token::<u32>(""));
400 assert!(!r.has_with_token::<u32>("other"));
401 assert_eq!(r.len(), 1);
402 assert!(!r.is_empty());
403 }
404
405 #[test]
406 fn has_returns_true_after_register_named() {
407 let mut r = ProviderRegistry::new();
408 r.register("primary", DynProvider::from_value(42u32));
409 assert!(!r.has::<u32>(), "default token should be absent");
410 assert!(r.has_with_token::<u32>("primary"));
411 assert!(!r.has_with_token::<u32>("replica"));
412 }
413
414 #[test]
415 fn multiple_tokens_same_type_coexist() {
416 let mut r = ProviderRegistry::new();
417 r.register("primary", DynProvider::from_value(1u32));
418 r.register("replica", DynProvider::from_value(2u32));
419 r.register("", DynProvider::from_value(0u32));
420 assert_eq!(r.len(), 3);
421 assert!(r.has::<u32>());
422 assert!(r.has_with_token::<u32>("primary"));
423 assert!(r.has_with_token::<u32>("replica"));
424 }
425
426 #[test]
427 fn duplicate_same_token_is_recorded() {
428 let mut r = ProviderRegistry::new();
429 r.register("", DynProvider::from_value(1u32));
430 r.register("", DynProvider::from_value(2u32));
431 assert_eq!(r.len(), 1); assert_eq!(r.duplicates().len(), 1);
433 }
434
435 #[test]
436 fn duplicate_different_tokens_not_recorded() {
437 let mut r = ProviderRegistry::new();
438 r.register("primary", DynProvider::from_value(1u32));
439 r.register("replica", DynProvider::from_value(2u32));
440 assert_eq!(
441 r.duplicates().len(),
442 0,
443 "different tokens are not duplicates"
444 );
445 }
446
447 #[test]
448 fn register_or_replace_does_not_record_duplicate() {
449 let mut r = ProviderRegistry::new();
450 r.register("", DynProvider::from_value(1u32));
451 r.register_or_replace("", DynProvider::from_value(2u32));
452 assert_eq!(r.duplicates().len(), 0);
453 }
454
455 #[test]
456 fn debug_shows_count() {
457 let mut r = ProviderRegistry::new();
458 r.register("", DynProvider::from_value(0u8));
459 let s = format!("{r:?}");
460 assert!(s.contains("ProviderRegistry"));
461 assert!(s.contains('1'));
462 }
463
464 #[test]
465 fn default_creates_empty() {
466 let r = ProviderRegistry::default();
467 assert!(r.is_empty());
468 }
469
470 #[test]
471 fn duplicate_label_includes_token_for_named() {
472 let label = ProviderRegistry::duplicate_label::<u32>("primary");
473 assert!(label.contains("primary"));
474 assert!(label.contains("u32"));
475 }
476
477 #[test]
478 fn duplicate_label_no_token_suffix_for_default() {
479 let label = ProviderRegistry::duplicate_label::<u32>("");
480 assert!(!label.contains('['), "default token adds no bracket suffix");
481 }
482}