1use std::marker::PhantomData;
2use std::ops::Deref;
3use std::sync::Arc;
4
5use async_trait::async_trait;
6use axum::extract::rejection::ExtensionRejection;
7use axum::extract::FromRequestParts;
8use axum::http::request::Parts;
9use axum::http::StatusCode;
10use axum::response::{IntoResponse, Response};
11use axum::Extension;
12
13use ruice::{AsyncServices, ServiceContainer};
14
15#[derive(Debug, thiserror::Error)]
16pub enum Error {
17 #[error("Service container is not available in this context: {0}")]
18 ServiceContainerNotAvailable(#[from] ExtensionRejection),
19
20 #[error("Could not find the service in the container, or could not resolve the service.")]
21 ServiceNotFound,
22}
23
24impl IntoResponse for Error {
25 fn into_response(self) -> Response {
26 (StatusCode::INTERNAL_SERVER_ERROR, format!("{}", self)).into_response()
27 }
28}
29
30pub struct Inject<I, C = ServiceContainer>
42where
43 I: ?Sized,
44 C: AsyncServices,
45{
46 interface: Arc<I>,
47 _phantom: PhantomData<fn() -> C>,
48}
49
50#[async_trait]
51impl<I, C, B> FromRequestParts<B> for Inject<I, C>
52where
53 I: ?Sized + Send + Sync + 'static,
54 C: AsyncServices + 'static,
55 B: Send + Sync,
56{
57 type Rejection = Error;
58
59 async fn from_request_parts(parts: &mut Parts, state: &B) -> Result<Self, Self::Rejection> {
60 let Extension(services): Extension<Arc<C>> =
61 Extension::from_request_parts(parts, state).await?;
62
63 Ok(Inject {
64 interface: services.get_async().await.ok_or(Error::ServiceNotFound)?,
65 _phantom: PhantomData,
66 })
67 }
68}
69
70impl<I, C> Deref for Inject<I, C>
71where
72 I: ?Sized + Send + Sync,
73 C: AsyncServices,
74{
75 type Target = I;
76
77 fn deref(&self) -> &Self::Target {
78 self.interface.as_ref()
79 }
80}