use std::sync::Arc;
#[cfg(feature = "metrics")]
use crate::metrics::MetricsCollector;
#[cfg(feature = "cache")]
use crate::traits::cache::Cache;
#[cfg(feature = "database")]
use crate::traits::database::DatabasePool;
#[cfg(feature = "jobs")]
use crate::traits::job::JobQueue;
#[cfg(feature = "email")]
use crate::traits::mailer::Mailer;
#[cfg(feature = "sessions")]
use crate::traits::session::SessionStore;
#[cfg(feature = "websocket")]
use crate::websocket::ConnectionManager;
#[derive(Clone)]
pub struct AppContext {
#[cfg(feature = "database")]
pub(crate) database: Option<Arc<dyn DatabasePool>>,
#[cfg(feature = "cache")]
pub(crate) cache: Option<Arc<dyn Cache>>,
#[cfg(feature = "sessions")]
pub(crate) sessions: Option<Arc<dyn SessionStore>>,
#[cfg(feature = "jobs")]
pub(crate) jobs: Option<Arc<dyn JobQueue>>,
#[cfg(feature = "websocket")]
pub(crate) websocket_manager: Option<Arc<ConnectionManager>>,
#[cfg(feature = "metrics")]
pub(crate) metrics: Option<Arc<MetricsCollector>>,
#[cfg(feature = "email")]
pub(crate) mailer: Option<Arc<dyn Mailer>>,
pub(crate) auth_provider: Option<Arc<dyn std::any::Any + Send + Sync>>,
}
#[cfg(feature = "auth")]
#[derive(Clone)]
pub(crate) struct AuthProviderExtension(pub Arc<dyn std::any::Any + Send + Sync>);
impl AppContext {
pub fn new() -> Self {
Self {
#[cfg(feature = "database")]
database: None,
#[cfg(feature = "cache")]
cache: None,
#[cfg(feature = "sessions")]
sessions: None,
#[cfg(feature = "jobs")]
jobs: None,
#[cfg(feature = "websocket")]
websocket_manager: None,
#[cfg(feature = "metrics")]
metrics: None,
#[cfg(feature = "email")]
mailer: None,
auth_provider: None,
}
}
pub fn builder() -> AppContextBuilder {
AppContextBuilder::new()
}
pub fn to_builder(&self) -> AppContextBuilder {
AppContextBuilder {
#[cfg(feature = "database")]
database: self.database.clone(),
#[cfg(feature = "cache")]
cache: self.cache.clone(),
#[cfg(feature = "sessions")]
sessions: self.sessions.clone(),
#[cfg(feature = "jobs")]
jobs: self.jobs.clone(),
#[cfg(feature = "websocket")]
websocket_manager: self.websocket_manager.clone(),
#[cfg(feature = "metrics")]
metrics: self.metrics.clone(),
#[cfg(feature = "email")]
mailer: self.mailer.clone(),
auth_provider: self.auth_provider.clone(),
}
}
#[cfg(feature = "database")]
pub fn database(&self) -> crate::error::Result<&Arc<dyn DatabasePool>> {
self.database
.as_ref()
.ok_or_else(|| crate::error::TidewayError::internal("Database pool not configured"))
}
#[cfg(feature = "database")]
pub fn database_opt(&self) -> Option<&Arc<dyn DatabasePool>> {
self.database.as_ref()
}
#[cfg(feature = "cache")]
pub fn cache(&self) -> crate::error::Result<&Arc<dyn Cache>> {
self.cache
.as_ref()
.ok_or_else(|| crate::error::TidewayError::internal("Cache not configured"))
}
#[cfg(feature = "cache")]
pub fn cache_opt(&self) -> Option<&Arc<dyn Cache>> {
self.cache.as_ref()
}
#[cfg(feature = "sessions")]
pub fn sessions(&self) -> crate::error::Result<&Arc<dyn SessionStore>> {
self.sessions
.as_ref()
.ok_or_else(|| crate::error::TidewayError::internal("Session store not configured"))
}
#[cfg(feature = "sessions")]
pub fn sessions_opt(&self) -> Option<&Arc<dyn SessionStore>> {
self.sessions.as_ref()
}
#[cfg(feature = "jobs")]
pub fn jobs(&self) -> crate::error::Result<&Arc<dyn JobQueue>> {
self.jobs
.as_ref()
.ok_or_else(|| crate::error::TidewayError::internal("Job queue not configured"))
}
#[cfg(feature = "jobs")]
pub fn jobs_opt(&self) -> Option<&Arc<dyn JobQueue>> {
self.jobs.as_ref()
}
#[cfg(feature = "websocket")]
pub fn websocket_manager(&self) -> crate::error::Result<Arc<ConnectionManager>> {
self.websocket_manager
.clone()
.ok_or_else(|| crate::error::TidewayError::internal("WebSocket manager not configured"))
}
#[cfg(feature = "websocket")]
pub fn websocket_manager_opt(&self) -> Option<Arc<ConnectionManager>> {
self.websocket_manager.clone()
}
#[cfg(feature = "metrics")]
pub fn metrics(&self) -> crate::error::Result<&Arc<MetricsCollector>> {
self.metrics
.as_ref()
.ok_or_else(|| crate::error::TidewayError::internal("Metrics collector not configured"))
}
#[cfg(feature = "metrics")]
pub fn metrics_opt(&self) -> Option<&Arc<MetricsCollector>> {
self.metrics.as_ref()
}
#[cfg(feature = "email")]
pub fn mailer(&self) -> crate::error::Result<&Arc<dyn Mailer>> {
self.mailer
.as_ref()
.ok_or_else(|| crate::error::TidewayError::internal("Mailer not configured"))
}
#[cfg(feature = "email")]
pub fn mailer_opt(&self) -> Option<&Arc<dyn Mailer>> {
self.mailer.as_ref()
}
pub fn auth_provider_opt<T: 'static>(&self) -> Option<&T> {
self.auth_provider
.as_ref()
.and_then(|p| p.downcast_ref::<T>())
}
pub fn auth_provider<T: 'static>(&self) -> crate::error::Result<&T> {
self.auth_provider_opt::<T>().ok_or_else(|| {
crate::error::TidewayError::internal("Auth provider not configured or wrong type")
})
}
#[cfg(feature = "auth")]
pub(crate) fn auth_provider_extension(&self) -> Option<AuthProviderExtension> {
self.auth_provider
.as_ref()
.map(Arc::clone)
.map(AuthProviderExtension)
}
#[cfg(feature = "database")]
pub fn sea_orm_connection(&self) -> crate::error::Result<sea_orm::DatabaseConnection> {
use crate::database::SeaOrmPool;
let pool = self.database()?;
let sea_orm_pool = pool.as_any().downcast_ref::<SeaOrmPool>().ok_or_else(|| {
crate::error::TidewayError::internal("Database pool is not SeaOrmPool")
})?;
Ok(sea_orm_pool.inner().clone())
}
}
impl Default for AppContext {
fn default() -> Self {
Self::new()
}
}
#[must_use = "builder does nothing until you call build()"]
pub struct AppContextBuilder {
#[cfg(feature = "database")]
database: Option<Arc<dyn DatabasePool>>,
#[cfg(feature = "cache")]
cache: Option<Arc<dyn Cache>>,
#[cfg(feature = "sessions")]
sessions: Option<Arc<dyn SessionStore>>,
#[cfg(feature = "jobs")]
jobs: Option<Arc<dyn JobQueue>>,
#[cfg(feature = "websocket")]
websocket_manager: Option<Arc<ConnectionManager>>,
#[cfg(feature = "metrics")]
metrics: Option<Arc<MetricsCollector>>,
#[cfg(feature = "email")]
mailer: Option<Arc<dyn Mailer>>,
auth_provider: Option<Arc<dyn std::any::Any + Send + Sync>>,
}
impl AppContextBuilder {
pub fn new() -> Self {
Self {
#[cfg(feature = "database")]
database: None,
#[cfg(feature = "cache")]
cache: None,
#[cfg(feature = "sessions")]
sessions: None,
#[cfg(feature = "jobs")]
jobs: None,
#[cfg(feature = "websocket")]
websocket_manager: None,
#[cfg(feature = "metrics")]
metrics: None,
#[cfg(feature = "email")]
mailer: None,
auth_provider: None,
}
}
#[cfg(feature = "database")]
pub fn with_database(mut self, pool: Arc<dyn DatabasePool>) -> Self {
self.database = Some(pool);
self
}
#[cfg(feature = "database")]
pub fn with_optional_database(self, pool: Option<Arc<dyn DatabasePool>>) -> Self {
if let Some(pool) = pool {
self.with_database(pool)
} else {
self
}
}
#[cfg(feature = "cache")]
pub fn with_cache(mut self, cache: Arc<dyn Cache>) -> Self {
self.cache = Some(cache);
self
}
#[cfg(feature = "sessions")]
pub fn with_sessions(mut self, sessions: Arc<dyn SessionStore>) -> Self {
self.sessions = Some(sessions);
self
}
#[cfg(feature = "jobs")]
pub fn with_job_queue(mut self, queue: Arc<dyn JobQueue>) -> Self {
self.jobs = Some(queue);
self
}
#[cfg(feature = "jobs")]
pub fn with_optional_job_queue(self, queue: Option<Arc<dyn JobQueue>>) -> Self {
if let Some(queue) = queue {
self.with_job_queue(queue)
} else {
self
}
}
#[cfg(feature = "websocket")]
pub fn with_websocket_manager(mut self, manager: Arc<ConnectionManager>) -> Self {
self.websocket_manager = Some(manager);
self
}
#[cfg(feature = "metrics")]
pub fn with_metrics(mut self, collector: Arc<MetricsCollector>) -> Self {
self.metrics = Some(collector);
self
}
#[cfg(feature = "email")]
pub fn with_mailer(mut self, mailer: Arc<dyn Mailer>) -> Self {
self.mailer = Some(mailer);
self
}
pub fn with_auth_provider<T: Send + Sync + 'static>(mut self, provider: Arc<T>) -> Self {
self.auth_provider = Some(provider as Arc<dyn std::any::Any + Send + Sync>);
self
}
pub fn with_optional_auth_provider<T: Send + Sync + 'static>(
self,
provider: Option<Arc<T>>,
) -> Self {
if let Some(provider) = provider {
self.with_auth_provider(provider)
} else {
self
}
}
pub fn build(self) -> AppContext {
AppContext {
#[cfg(feature = "database")]
database: self.database,
#[cfg(feature = "cache")]
cache: self.cache,
#[cfg(feature = "sessions")]
sessions: self.sessions,
#[cfg(feature = "jobs")]
jobs: self.jobs,
#[cfg(feature = "websocket")]
websocket_manager: self.websocket_manager,
#[cfg(feature = "metrics")]
metrics: self.metrics,
#[cfg(feature = "email")]
mailer: self.mailer,
auth_provider: self.auth_provider,
}
}
}
impl Default for AppContextBuilder {
fn default() -> Self {
Self::new()
}
}