Skip to main content

renox_core/
provided.rs

1//! Values the app provides with `App::provide`.
2
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::ops::Deref;
6use std::sync::Arc;
7
8use axum::extract::FromRequestParts;
9use axum::http::request::Parts;
10
11use crate::{AppState, Error};
12
13pub(crate) type ProvidedMap = Arc<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>;
14
15impl AppState {
16    /// A value given to `App::provide`, if one of type `T` was.
17    pub fn provided<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
18        self.provided
19            .get(&TypeId::of::<T>())
20            .cloned()
21            .and_then(|value| value.downcast::<T>().ok())
22    }
23}
24
25/// The app's own shared values (an API client, a price list), available in
26/// handlers, jobs, listeners, commands and scheduled tasks.
27///
28/// ```
29/// # use renox::prelude::*;
30/// use renox::Provided;
31///
32/// #[derive(Clone)]
33/// struct Payments {
34///     api_key: String,
35/// }
36///
37/// async fn checkout(payments: Provided<Payments>) -> String {
38///     format!("charging with key {}…", &payments.api_key[..3])
39/// }
40///
41/// async fn in_a_job(state: AppState) {
42///     let payments = state.provided::<Payments>().expect("provided at boot");
43/// #   let _ = payments;
44/// }
45///
46/// # let _ =
47/// App::new().provide(Payments { api_key: "sk_test_123".into() })
48/// # ;
49/// ```
50///
51/// A handler asking for a type that wasn't provided answers 500, naming the type.
52pub struct Provided<T>(pub Arc<T>);
53
54impl<T> Deref for Provided<T> {
55    type Target = T;
56
57    fn deref(&self) -> &T {
58        &self.0
59    }
60}
61
62impl<T: Send + Sync + 'static> FromRequestParts<AppState> for Provided<T> {
63    type Rejection = Error;
64
65    async fn from_request_parts(_: &mut Parts, state: &AppState) -> Result<Self, Error> {
66        state.provided::<T>().map(Provided).ok_or_else(|| {
67            Error::Internal(anyhow::anyhow!(
68                "no {} was provided: add `App::provide(…)`",
69                std::any::type_name::<T>()
70            ))
71        })
72    }
73}