Skip to main content

nestforge_core/
inject.rs

1use std::{ops::Deref, sync::Arc};
2
3use anyhow::Result;
4use axum::{
5    extract::{FromRequestParts, State},
6    http::request::Parts,
7};
8
9use crate::{Container, HttpException};
10
11/*
12Inject<T> = DI extractor wrapper
13
14Now users can write:
15users: Inject<UsersService>
16
17and NestForge resolves it from the shared DI container automatically.
18*/
19pub struct Inject<T>(Arc<T>);
20
21impl<T> Inject<T>
22where
23    T: Send + Sync + 'static,
24{
25    /*
26    Manual resolve helper (still useful internally/tests)
27    */
28    pub fn from(container: &Container) -> Result<Self> {
29        let inner = container.resolve::<T>()?;
30        Ok(Self(inner))
31    }
32
33    pub fn into_inner(self) -> Arc<T> {
34        self.0
35    }
36}
37
38impl<T> Deref for Inject<T> {
39    type Target = T;
40
41    fn deref(&self) -> &Self::Target {
42        &self.0
43    }
44}
45
46/*
47Magic extractor:
48Axum gives us Container state -> we resolve T from DI -> handler gets Inject<T>
49*/
50impl<T> FromRequestParts<Container> for Inject<T>
51where
52    T: Send + Sync + 'static,
53{
54    type Rejection = HttpException;
55
56    async fn from_request_parts(
57        parts: &mut Parts,
58        state: &Container,
59    ) -> Result<Self, Self::Rejection> {
60        let State(container): State<Container> = State::from_request_parts(parts, state)
61            .await
62            .map_err(|_| HttpException::internal_server_error("Container state not available"))?;
63
64        Self::from(&container)
65            .map_err(|_| HttpException::internal_server_error("Dependency not registered"))
66    }
67}