//! Web application context and dependency injection. //! //! Provides [`WebContext`] with all HTTP handler dependencies: //! OAuth config, storage, templates, and i18n. Uses Axum's `FromRef`. //! //! ## Dependency Injection Pattern //! The [`WebContext`] acts as a service container that implements Axum's //! `FromRef` trait, allowing handlers to extract specific dependencies //! (like `IdentityResolver`, `KeyProvider`) directly from function parameters //! without manual dependency passing. //! //! ## Available Dependencies //! - OAuth client configuration and request storage //! - AT Protocol identity resolution and key management //! - Escrow record storage backends //! - Template engines and i18n localization //! - HTTP client for external requests use atproto_identity::{ key::KeyProvider, resolve::SharedIdentityResolver, storage::DidDocumentStorage, }; use atproto_oauth::storage::OAuthRequestStorage; use atproto_oauth_axum::state::OAuthClientConfig; use axum::extract::FromRef; use axum_template::engine::Engine; use std::{ops::Deref, sync::Arc}; use unic_langid::LanguageIdentifier; #[cfg(feature = "reload")] use minijinja_autoreload::AutoReloader; #[cfg(feature = "reload")] pub type AppEngine = Engine; #[cfg(feature = "embed")] use minijinja::Environment; #[cfg(feature = "embed")] pub type AppEngine = Engine>; use crate::{config::Config, i18n::Locales, storage::escrow::EscrowRecordDataSource}; pub struct I18nContext { pub(crate) supported_languages: Vec, pub(crate) locales: Locales, } pub struct InnerWebContext { pub(crate) engine: AppEngine, pub(crate) http_client: reqwest::Client, pub(crate) config: Config, pub(crate) i18n_context: I18nContext, pub(crate) oauth_client_config: OAuthClientConfig, pub(crate) identity_resolver: SharedIdentityResolver, pub(crate) key_provider: Arc, pub(crate) oauth_storage: Arc, pub(crate) document_storage: Arc, pub(crate) escrow_storage: Arc, } #[derive(Clone, FromRef)] pub struct WebContext(pub(crate) Arc); impl Deref for WebContext { type Target = InnerWebContext; fn deref(&self) -> &Self::Target { &self.0 } } impl WebContext { #[allow(clippy::too_many_arguments)] pub fn new( engine: AppEngine, http_client: &reqwest::Client, config: Config, i18n_context: I18nContext, oauth_client_config: OAuthClientConfig, identity_resolver: SharedIdentityResolver, key_provider: Arc, oauth_storage: Arc, document_storage: Arc, escrow_storage: Arc, ) -> Self { Self(Arc::new(InnerWebContext { engine, http_client: http_client.clone(), config, i18n_context, oauth_client_config, identity_resolver, key_provider, oauth_storage, document_storage, escrow_storage, })) } } impl I18nContext { pub fn new(supported_languages: Vec, locales: Locales) -> Self { Self { supported_languages, locales, } } } impl FromRef for SharedIdentityResolver { fn from_ref(context: &WebContext) -> Self { context.0.identity_resolver.clone() } } impl FromRef for Arc { fn from_ref(context: &WebContext) -> Self { context.0.key_provider.clone() } } impl FromRef for OAuthClientConfig { fn from_ref(context: &WebContext) -> Self { context.0.oauth_client_config.clone() } } impl FromRef for Arc { fn from_ref(context: &WebContext) -> Self { context.0.document_storage.clone() } } impl FromRef for Arc { fn from_ref(context: &WebContext) -> Self { context.0.identity_resolver.0.clone() } } impl FromRef for Arc { fn from_ref(context: &WebContext) -> Self { context.0.document_storage.clone() } } impl FromRef for Arc { fn from_ref(context: &WebContext) -> Self { context.0.identity_resolver.0.clone() } }