oxide_framework_core/
extract.rs1use axum::extract::FromRequestParts;
2use axum::http::StatusCode;
3use axum::http::request::Parts;
4use axum::response::{IntoResponse, Response};
5use std::sync::Arc;
6
7use crate::config::AppConfig;
8use crate::state::AppState;
9
10pub struct Config(pub Arc<AppConfig>);
22
23impl<S: Send + Sync> FromRequestParts<S> for Config {
24 type Rejection = StateNotFound;
25
26 async fn from_request_parts(
27 parts: &mut Parts,
28 _state: &S,
29 ) -> Result<Self, Self::Rejection> {
30 parts
31 .extensions
32 .get::<AppState>()
33 .map(|s| Config(s.config.clone()))
34 .ok_or(StateNotFound("AppConfig"))
35 }
36}
37
38pub struct Data<T: Send + Sync + 'static>(pub Arc<T>);
55
56impl<S: Send + Sync, T: Send + Sync + 'static> FromRequestParts<S> for Data<T> {
57 type Rejection = StateNotFound;
58
59 async fn from_request_parts(
60 parts: &mut Parts,
61 _state: &S,
62 ) -> Result<Self, Self::Rejection> {
63 let app_state = parts
64 .extensions
65 .get::<AppState>()
66 .ok_or(StateNotFound("AppState"))?;
67
68 app_state
69 .get::<T>()
70 .map(Data)
71 .ok_or(StateNotFound(std::any::type_name::<T>()))
72 }
73}
74
75pub struct Inject<T: Send + Sync + 'static>(pub Arc<T>);
93
94impl<S: Send + Sync, T: Send + Sync + 'static> FromRequestParts<S> for Inject<T> {
95 type Rejection = StateNotFound;
96
97 async fn from_request_parts(
98 parts: &mut Parts,
99 _state: &S,
100 ) -> Result<Self, Self::Rejection> {
101 let app_state = parts
102 .extensions
103 .get::<AppState>()
104 .ok_or(StateNotFound("AppState"))?;
105
106 app_state
107 .get::<T>()
108 .map(Inject)
109 .ok_or(StateNotFound(std::any::type_name::<T>()))
110 }
111}
112
113#[derive(Debug)]
115pub struct StateNotFound(pub &'static str);
116
117impl std::fmt::Display for StateNotFound {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(f, "state not found: {}", self.0)
120 }
121}
122
123impl std::error::Error for StateNotFound {}
124
125impl IntoResponse for StateNotFound {
126 fn into_response(self) -> Response {
127 (
128 StatusCode::INTERNAL_SERVER_ERROR,
129 format!("internal error: missing state ({})", self.0),
130 )
131 .into_response()
132 }
133}
134
135pub struct Scoped<T>(pub T);
140
141impl<S, T> axum::extract::FromRequestParts<S> for Scoped<T>
142where
143 S: Send + Sync,
144 T: Clone + Send + Sync + 'static,
145{
146 type Rejection = axum::response::Response;
147
148 async fn from_request_parts(
149 parts: &mut axum::http::request::Parts,
150 _state: &S,
151 ) -> Result<Self, Self::Rejection> {
152 parts
153 .extensions
154 .get::<T>()
155 .cloned()
156 .map(Scoped)
157 .ok_or_else(|| {
158 crate::ApiResponse::<()>::error(
159 axum::http::StatusCode::INTERNAL_SERVER_ERROR,
160 format!(
161 "Missing scoped dependency: {}",
162 std::any::type_name::<T>()
163 )
164 ).into_response()
165 })
166 }
167}
168
169