1use crate::app::App;
43use crate::body::Body;
44use crate::error::ApiError;
45use crate::extract::FromRequestParts;
46use crate::response::IntoResponse;
47use http::request::Parts;
48use siderite_openapi::{Operation, SchemaRegistry};
49use std::any::{Any, TypeId, type_name};
50use std::cell::RefCell;
51use std::collections::HashMap;
52use std::convert::Infallible;
53use std::fmt::Display;
54use std::future::Future;
55use std::ops::Deref;
56use std::pin::Pin;
57use std::sync::{Arc, Mutex, PoisonError};
58use std::task::{Context, Poll};
59use thiserror::Error;
60use tower::{Layer, Service, ServiceExt};
61
62type BoxFut<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
63type Erased = Arc<dyn Any + Send + Sync>;
64
65#[derive(Debug, Clone, Error)]
68#[non_exhaustive]
69pub enum DependencyError {
70 #[error("dependency cycle detected: {}", .chain.join(" -> "))]
72 Cycle {
73 chain: Vec<&'static str>,
75 },
76 #[error("no value of type `{type_name}` was provided with App::provide")]
78 NotProvided {
79 type_name: &'static str,
81 },
82}
83
84pub trait Dependency: Send + Sync + Sized + 'static {
86 fn resolve(ctx: &mut ResolveContext<'_>)
89 -> impl Future<Output = Result<Self, ApiError>> + Send;
90
91 fn describe(_op: &mut Operation, _registry: &mut SchemaRegistry) {}
93}
94
95#[derive(Debug, Clone)]
97pub struct RequestHead {
98 pub method: http::Method,
100 pub uri: http::Uri,
102 pub headers: http::HeaderMap,
104}
105
106impl From<&Parts> for RequestHead {
107 fn from(parts: &Parts) -> Self {
108 Self {
109 method: parts.method.clone(),
110 uri: parts.uri.clone(),
111 headers: parts.headers.clone(),
112 }
113 }
114}
115
116pub trait TeardownOutput: Send + 'static {
119 fn failure(self) -> Option<String>;
121}
122
123impl TeardownOutput for () {
124 fn failure(self) -> Option<String> {
125 None
126 }
127}
128
129impl<E: Display + Send + 'static> TeardownOutput for Result<(), E> {
130 fn failure(self) -> Option<String> {
131 self.err().map(|e| e.to_string())
132 }
133}
134
135type Teardown = BoxFut<'static, ()>;
136
137#[derive(Default)]
138struct ScopeState {
139 cache: HashMap<TypeId, Erased>,
140 stack: Vec<(TypeId, &'static str)>,
141 teardowns: Vec<Teardown>,
142}
143
144#[derive(Clone, Default)]
146pub(crate) struct RequestScope(Arc<Mutex<ScopeState>>);
147
148impl RequestScope {
149 fn with<R>(&self, f: impl FnOnce(&mut ScopeState) -> R) -> R {
150 f(&mut self.0.lock().unwrap_or_else(PoisonError::into_inner))
151 }
152
153 fn of(parts: &mut Parts) -> Self {
154 if let Some(scope) = parts.extensions.get::<Self>() {
155 return scope.clone();
156 }
157 let scope = Self::default();
158 parts.extensions.insert(scope.clone());
159 scope
160 }
161
162 fn spawn_teardowns(&self) {
164 let list = self.with(|s| std::mem::take(&mut s.teardowns));
165 if list.is_empty() {
166 return;
167 }
168 let Ok(runtime) = tokio::runtime::Handle::try_current() else {
169 tracing::warn!("no tokio runtime available; dependency teardown skipped");
170 return;
171 };
172 runtime.spawn(async move {
173 for teardown in list.into_iter().rev() {
174 if let Err(err) = tokio::spawn(teardown).await {
175 tracing::error!(error = %err, "dependency teardown panicked");
176 }
177 }
178 });
179 }
180}
181
182type OverrideFn =
183 Arc<dyn Fn(RequestHead) -> BoxFut<'static, Result<Erased, ApiError>> + Send + Sync>;
184type GlobalFn =
185 Arc<dyn for<'a> Fn(&'a mut Parts) -> BoxFut<'a, Result<(), ApiError>> + Send + Sync>;
186
187#[derive(Default)]
189pub(crate) struct DiRegistry {
190 provided: HashMap<TypeId, Erased>,
191 overrides: HashMap<TypeId, OverrideFn>,
192 globals: Vec<GlobalFn>,
193}
194
195impl DiRegistry {
196 pub(crate) fn is_empty(&self) -> bool {
197 self.provided.is_empty() && self.overrides.is_empty() && self.globals.is_empty()
198 }
199}
200
201#[derive(Clone)]
203struct Active(Arc<ActiveNode>);
204
205struct ActiveNode {
206 registry: Arc<DiRegistry>,
207 parent: Option<Active>,
208}
209
210impl Active {
211 fn find<R>(&self, f: impl Fn(&DiRegistry) -> Option<R>) -> Option<R> {
212 let mut node = Some(&self.0);
213 while let Some(n) = node {
214 if let Some(found) = f(&n.registry) {
215 return Some(found);
216 }
217 node = n.parent.as_ref().map(|p| &p.0);
218 }
219 None
220 }
221}
222
223fn downcast<U: Send + Sync + 'static>(value: Erased) -> Result<Arc<U>, ApiError> {
224 value.downcast::<U>().map_err(|_| {
225 ApiError::internal(format!(
226 "dependency type mismatch for `{}`",
227 type_name::<U>()
228 ))
229 })
230}
231
232fn cycle_chain(state: &ScopeState, tid: TypeId, name: &'static str) -> Option<Vec<&'static str>> {
234 let start = state.stack.iter().position(|(t, _)| *t == tid)?;
235 let mut chain: Vec<&'static str> = state.stack[start..].iter().map(|(_, n)| *n).collect();
236 chain.push(name);
237 Some(chain)
238}
239
240async fn resolve_arc<U: Dependency>(parts: &mut Parts) -> Result<Arc<U>, ApiError> {
241 let tid = TypeId::of::<U>();
242 let name = type_name::<U>();
243 let scope = RequestScope::of(parts);
244 if let Some(hit) = scope.with(|s| s.cache.get(&tid).cloned()) {
245 return downcast(hit);
246 }
247 if let Some(chain) = scope.with(|s| cycle_chain(s, tid, name)) {
248 return Err(ApiError::internal(DependencyError::Cycle { chain }));
249 }
250 let active = parts.extensions.get::<Active>().cloned();
251 if let Some(value) = active
252 .as_ref()
253 .and_then(|a| a.find(|r| r.provided.get(&tid).cloned()))
254 {
255 return downcast(value);
256 }
257 let override_fn = active
258 .as_ref()
259 .and_then(|a| a.find(|r| r.overrides.get(&tid).cloned()));
260 scope.with(|s| s.stack.push((tid, name)));
261 let outcome = match override_fn {
262 Some(f) => f(RequestHead::from(&*parts)).await,
263 None => {
264 let mut ctx = ResolveContext { parts };
265 U::resolve(&mut ctx).await.map(|v| Arc::new(v) as Erased)
266 }
267 };
268 scope.with(|s| s.stack.pop());
269 let value = outcome?;
270 scope.with(|s| s.cache.insert(tid, Arc::clone(&value)));
271 downcast(value)
272}
273
274pub struct ResolveContext<'a> {
276 parts: &'a mut Parts,
277}
278
279impl<'a> ResolveContext<'a> {
280 pub fn parts(&mut self) -> &mut Parts {
283 self.parts
284 }
285
286 pub fn resolve<U: Dependency>(&mut self) -> BoxFut<'_, Result<Arc<U>, ApiError>> {
288 Box::pin(resolve_arc::<U>(self.parts))
289 }
290
291 pub fn on_teardown<F>(&mut self, teardown: F)
294 where
295 F: Future + Send + 'static,
296 F::Output: TeardownOutput,
297 {
298 let wrapped: Teardown = Box::pin(async move {
299 if let Some(message) = teardown.await.failure() {
300 tracing::error!(error = %message, "dependency teardown failed");
301 }
302 });
303 RequestScope::of(self.parts).with(|s| s.teardowns.push(wrapped));
304 }
305}
306
307#[derive(Debug)]
309pub struct Depends<T>(pub Arc<T>);
310
311impl<T> Clone for Depends<T> {
312 fn clone(&self) -> Self {
313 Self(Arc::clone(&self.0))
314 }
315}
316
317impl<T> Deref for Depends<T> {
318 type Target = T;
319
320 fn deref(&self) -> &T {
321 &self.0
322 }
323}
324
325thread_local! {
326 static DESCRIBING: RefCell<Vec<TypeId>> = const { RefCell::new(Vec::new()) };
327}
328
329impl<T: Dependency> FromRequestParts for Depends<T> {
330 async fn from_request_parts(parts: &mut Parts) -> Result<Self, ApiError> {
331 resolve_arc::<T>(parts).await.map(Depends)
332 }
333
334 fn describe(op: &mut Operation, registry: &mut SchemaRegistry) {
335 let tid = TypeId::of::<T>();
336 let entered = DESCRIBING.with(|d| {
337 let mut stack = d.borrow_mut();
338 if stack.contains(&tid) {
339 false
340 } else {
341 stack.push(tid);
342 true
343 }
344 });
345 if entered {
346 T::describe(op, registry);
347 DESCRIBING.with(|d| d.borrow_mut().pop());
348 }
349 }
350}
351
352#[derive(Debug)]
355pub struct Provided<T>(pub Arc<T>);
356
357impl<T> Clone for Provided<T> {
358 fn clone(&self) -> Self {
359 Self(Arc::clone(&self.0))
360 }
361}
362
363impl<T> Deref for Provided<T> {
364 type Target = T;
365
366 fn deref(&self) -> &T {
367 &self.0
368 }
369}
370
371impl<T: Send + Sync + 'static> FromRequestParts for Provided<T> {
372 async fn from_request_parts(parts: &mut Parts) -> Result<Self, ApiError> {
373 let tid = TypeId::of::<T>();
374 let found = parts
375 .extensions
376 .get::<Active>()
377 .and_then(|a| a.find(|r| r.provided.get(&tid).cloned()));
378 match found {
379 Some(value) => downcast(value).map(Provided),
380 None => Err(ApiError::internal(DependencyError::NotProvided {
381 type_name: type_name::<T>(),
382 })),
383 }
384 }
385}
386
387impl App {
388 #[must_use]
391 pub fn provide<T: Send + Sync + 'static>(self, value: T) -> Self {
392 self.provide_arc(Arc::new(value))
393 }
394
395 #[must_use]
398 pub fn provide_arc<T: Send + Sync + 'static>(mut self, value: Arc<T>) -> Self {
399 self.di.provided.insert(TypeId::of::<T>(), value);
400 self
401 }
402
403 #[must_use]
407 pub fn override_dependency<T, F, Fut>(mut self, f: F) -> Self
408 where
409 T: Dependency,
410 F: Fn(RequestHead) -> Fut + Send + Sync + 'static,
411 Fut: Future<Output = Result<T, ApiError>> + Send + 'static,
412 {
413 let erased: OverrideFn = Arc::new(move |head| {
414 let fut = f(head);
415 Box::pin(async move { fut.await.map(|v| Arc::new(v) as Erased) })
416 });
417 self.di.overrides.insert(TypeId::of::<T>(), erased);
418 self
419 }
420
421 #[must_use]
423 pub fn override_value<T: Dependency + Clone>(self, value: T) -> Self {
424 self.override_dependency(move |_| {
425 let value = value.clone();
426 async move { Ok(value) }
427 })
428 }
429
430 #[must_use]
433 pub fn dependency<T: Dependency>(mut self) -> Self {
434 self.di.globals.push(Arc::new(|parts: &mut Parts| {
435 Box::pin(async move { resolve_arc::<T>(parts).await.map(|_| ()) })
436 }));
437 self
438 }
439}
440
441pub(crate) fn install(router: axum::Router, registry: DiRegistry) -> axum::Router {
444 router.layer(DiLayer(Arc::new(registry)))
445}
446
447#[derive(Clone)]
448struct DiLayer(Arc<DiRegistry>);
449
450impl<S> Layer<S> for DiLayer {
451 type Service = DiService<S>;
452
453 fn layer(&self, inner: S) -> DiService<S> {
454 DiService {
455 inner,
456 registry: Arc::clone(&self.0),
457 }
458 }
459}
460
461#[derive(Clone)]
462struct DiService<S> {
463 inner: S,
464 registry: Arc<DiRegistry>,
465}
466
467impl<S> Service<axum::extract::Request> for DiService<S>
468where
469 S: Service<axum::extract::Request, Response = axum::response::Response, Error = Infallible>
470 + Clone
471 + Send
472 + 'static,
473 S::Future: Send,
474{
475 type Response = axum::response::Response;
476 type Error = Infallible;
477 type Future = BoxFut<'static, Result<Self::Response, Infallible>>;
478
479 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Infallible>> {
480 self.inner.poll_ready(cx)
481 }
482
483 fn call(&mut self, req: axum::extract::Request) -> Self::Future {
484 let ready = self.inner.clone();
485 let inner = std::mem::replace(&mut self.inner, ready);
486 let registry = Arc::clone(&self.registry);
487 Box::pin(async move {
488 let (mut parts, body) = req.into_parts();
489 crate::middleware::note_matched_path(&parts);
490 let parent = parts.extensions.get::<Active>().cloned();
491 let node = ActiveNode {
492 registry: Arc::clone(®istry),
493 parent,
494 };
495 parts.extensions.insert(Active(Arc::new(node)));
496 let owned = match parts.extensions.get::<RequestScope>() {
497 Some(_) => None,
498 None => {
499 let scope = RequestScope::default();
500 parts.extensions.insert(scope.clone());
501 Some(scope)
502 }
503 };
504 let response = match run_globals(®istry, &mut parts).await {
505 Err(err) => err.into_response().map(Body::into_inner),
506 Ok(()) => {
507 inner
508 .oneshot(http::Request::from_parts(parts, body))
509 .await?
510 }
511 };
512 if let Some(scope) = owned {
513 scope.spawn_teardowns();
514 }
515 Ok(response)
516 })
517 }
518}
519
520async fn run_globals(registry: &DiRegistry, parts: &mut Parts) -> Result<(), ApiError> {
521 for global in ®istry.globals {
522 global(parts).await?;
523 }
524 Ok(())
525}