modo/service/extractor.rs
1use std::sync::Arc;
2
3use axum::extract::{FromRef, FromRequestParts};
4use http::request::Parts;
5
6use crate::service::AppState;
7
8/// Axum extractor that retrieves a service `T` from the application's service registry.
9///
10/// The inner value is an `Arc<T>`, so cloning the extractor is cheap. `T` must have
11/// been registered on the [`Registry`](super::Registry) before [`Registry::into_state`]
12/// was called.
13///
14/// [`Registry::into_state`]: super::Registry::into_state
15///
16/// # Errors
17///
18/// Rejects with [`crate::Error::internal`] (HTTP `500 Internal Server Error`) when `T`
19/// was not registered. The error message includes the full type name of `T`.
20///
21/// # Example
22///
23/// ```
24/// use modo::service::Service;
25///
26/// struct MyService;
27///
28/// async fn handler(Service(svc): Service<MyService>) {
29/// // svc is Arc<MyService>
30/// }
31/// ```
32pub struct Service<T>(pub Arc<T>);
33
34impl<S, T> FromRequestParts<S> for Service<T>
35where
36 S: Send + Sync,
37 T: Send + Sync + 'static,
38 AppState: FromRef<S>,
39{
40 type Rejection = crate::error::Error;
41
42 async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
43 let app_state = AppState::from_ref(state);
44
45 app_state.get::<T>().map(Service).ok_or_else(|| {
46 crate::error::Error::internal(format!(
47 "service not found in registry: {}",
48 std::any::type_name::<T>()
49 ))
50 })
51 }
52}