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::{framework_log, 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        framework_log(format!("Injecting {}.", std::any::type_name::<T>()));
30        let inner = container.resolve::<T>()?;
31        Ok(Self(inner))
32    }
33
34    pub fn into_inner(self) -> Arc<T> {
35        self.0
36    }
37}
38
39impl<T> Deref for Inject<T> {
40    type Target = T;
41
42    fn deref(&self) -> &Self::Target {
43        &self.0
44    }
45}
46
47/*
48Magic extractor:
49Axum gives us Container state -> we resolve T from DI -> handler gets Inject<T>
50*/
51impl<T> FromRequestParts<Container> for Inject<T>
52where
53    T: Send + Sync + 'static,
54{
55    type Rejection = HttpException;
56
57    async fn from_request_parts(
58        parts: &mut Parts,
59        state: &Container,
60    ) -> Result<Self, Self::Rejection> {
61        let State(container): State<Container> = State::from_request_parts(parts, state)
62            .await
63            .map_err(|_| HttpException::internal_server_error("Container state not available"))?;
64
65        Self::from(&container).map_err(|err| {
66            HttpException::internal_server_error(format!(
67                "Failed to resolve dependency `{}`: {}",
68                std::any::type_name::<T>(),
69                err
70            ))
71        })
72    }
73}
74
75impl<T> AsRef<T> for Inject<T> {
76    fn as_ref(&self) -> &T {
77        &self.0
78    }
79}