Skip to main content

serverust_core/
container.rs

1//! Container tipado de Dependency Injection.
2//!
3//! Armazena serviços como `Arc<T: ?Sized>` chaveados pelo `TypeId` do próprio
4//! `Arc<T>`. Resolução é typesafe em compile-time via [`axum::extract::FromRef`]:
5//! qualquer handler que precise de `State<Arc<dyn Trait>>` recebe o `Arc`
6//! clonado a partir deste container.
7
8use std::any::{Any, TypeId};
9use std::collections::HashMap;
10use std::sync::Arc;
11
12use axum::extract::FromRef;
13
14/// State do `axum::Router`: mapa `TypeId` → `Arc<dyn Any>` carregando os
15/// serviços registrados via [`crate::App::provide`].
16#[derive(Clone, Default)]
17pub struct Container {
18    services: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
19}
20
21impl Container {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    /// Insere (ou substitui) `Arc<T>` no container. Chave é o `TypeId` de
27    /// `Arc<T>`, então `Arc<dyn TraitA>` e `Arc<dyn TraitB>` ocupam slots
28    /// distintos mesmo apontando para a mesma implementação concreta.
29    pub fn insert<T: ?Sized + Send + Sync + 'static>(&mut self, value: Arc<T>) {
30        self.services
31            .insert(TypeId::of::<Arc<T>>(), Arc::new(value));
32    }
33
34    /// Recupera um `Arc<T>` previamente inserido, ou `None` se ausente.
35    pub fn get<T: ?Sized + Send + Sync + 'static>(&self) -> Option<Arc<T>> {
36        let any_arc = self.services.get(&TypeId::of::<Arc<T>>())?;
37        any_arc.downcast_ref::<Arc<T>>().cloned()
38    }
39}
40
41// Blanket: qualquer `Arc<T>` pode ser extraído do Container via axum's State.
42// Possível pelo orphan rule porque `Container` é local e aparece como
43// parâmetro de tipo da trait `FromRef`.
44impl<T: ?Sized + Send + Sync + 'static> FromRef<Container> for Arc<T> {
45    fn from_ref(container: &Container) -> Arc<T> {
46        container.get::<T>().unwrap_or_else(|| {
47            panic!(
48                "service Arc<{}> not registered in App container",
49                std::any::type_name::<T>()
50            )
51        })
52    }
53}
54
55/// Marker trait emitida por `#[injectable]`. Não impõe método; apenas
56/// confirma intenção de injeção e habilita verificações de tipo nos testes.
57pub trait Injectable {}