1use std::any::{Any, TypeId};
8use std::collections::HashMap;
9use std::sync::Arc;
10
11use parking_lot::RwLock;
12
13use crate::auth::AuthManager;
14use crate::cache::CacheStore;
15use crate::config::{AppConfig, DatabaseConfig, MailConfig, QueueConfig, SessionConfig};
16use crate::event::EventBus;
17use crate::mail::MailerHandle;
18use crate::queue::QueueHandle;
19use crate::storage::StorageManager;
20
21pub type Pool = sqlx::PgPool;
22
23#[derive(Clone)]
24pub struct Container {
25 inner: Arc<ContainerInner>,
26}
27
28pub struct ContainerInner {
29 pub app: AppConfig,
30 pub db: DatabaseConfig,
31 pub session_cfg: SessionConfig,
32 pub mail_cfg: MailConfig,
33 pub queue_cfg: QueueConfig,
34 pub pool: Pool,
35 pub cache: CacheStore,
36 pub mailer: MailerHandle,
37 pub queue: QueueHandle,
38 pub storage: StorageManager,
39 pub events: EventBus,
40 pub auth: AuthManager,
41 bindings: RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
42}
43
44impl Container {
45 pub fn app(&self) -> &AppConfig {
46 &self.inner.app
47 }
48 pub fn pool(&self) -> &Pool {
49 &self.inner.pool
50 }
51 pub fn cache(&self) -> &CacheStore {
52 &self.inner.cache
53 }
54 pub fn mailer(&self) -> &MailerHandle {
55 &self.inner.mailer
56 }
57 pub fn queue(&self) -> &QueueHandle {
58 &self.inner.queue
59 }
60 pub fn storage(&self) -> &StorageManager {
61 &self.inner.storage
62 }
63 pub fn events(&self) -> &EventBus {
64 &self.inner.events
65 }
66 pub fn auth(&self) -> &AuthManager {
67 &self.inner.auth
68 }
69
70 pub fn resolve<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
72 let bindings = self.inner.bindings.read();
73 bindings
74 .get(&TypeId::of::<T>())
75 .and_then(|v| v.clone().downcast::<T>().ok())
76 }
77
78 pub fn bind<T: Send + Sync + 'static>(&self, value: T) {
80 let mut bindings = self.inner.bindings.write();
81 bindings.insert(TypeId::of::<T>(), Arc::new(value));
82 }
83}
84
85pub struct ContainerBuilder {
86 pub app: AppConfig,
87 pub db: DatabaseConfig,
88 pub session_cfg: SessionConfig,
89 pub mail_cfg: MailConfig,
90 pub queue_cfg: QueueConfig,
91 pub pool: Option<Pool>,
92 pub cache: Option<CacheStore>,
93 pub mailer: Option<MailerHandle>,
94 pub queue: Option<QueueHandle>,
95 pub storage: Option<StorageManager>,
96 pub events: Option<EventBus>,
97 pub auth: Option<AuthManager>,
98}
99
100impl ContainerBuilder {
101 pub fn from_env() -> Self {
102 Self {
103 app: AppConfig::from_env(),
104 db: DatabaseConfig::from_env(),
105 session_cfg: SessionConfig::from_env(),
106 mail_cfg: MailConfig::from_env(),
107 queue_cfg: QueueConfig::from_env(),
108 pool: None,
109 cache: None,
110 mailer: None,
111 queue: None,
112 storage: None,
113 events: None,
114 auth: None,
115 }
116 }
117
118 pub fn pool(mut self, pool: Pool) -> Self {
119 self.pool = Some(pool);
120 self
121 }
122 pub fn cache(mut self, c: CacheStore) -> Self {
123 self.cache = Some(c);
124 self
125 }
126 pub fn mailer(mut self, m: MailerHandle) -> Self {
127 self.mailer = Some(m);
128 self
129 }
130 pub fn queue(mut self, q: QueueHandle) -> Self {
131 self.queue = Some(q);
132 self
133 }
134 pub fn storage(mut self, s: StorageManager) -> Self {
135 self.storage = Some(s);
136 self
137 }
138 pub fn events(mut self, e: EventBus) -> Self {
139 self.events = Some(e);
140 self
141 }
142 pub fn auth(mut self, a: AuthManager) -> Self {
143 self.auth = Some(a);
144 self
145 }
146
147 pub fn build(self) -> Container {
148 let pool = self.pool.expect("ContainerBuilder requires a database pool");
149 let inner = ContainerInner {
150 app: self.app,
151 db: self.db,
152 session_cfg: self.session_cfg,
153 mail_cfg: self.mail_cfg,
154 queue_cfg: self.queue_cfg,
155 cache: self.cache.unwrap_or_else(CacheStore::null),
156 mailer: self.mailer.unwrap_or_else(MailerHandle::null),
157 queue: self.queue.unwrap_or_else(|| QueueHandle::in_memory(pool.clone())),
158 storage: self.storage.unwrap_or_else(StorageManager::local_default),
159 events: self.events.unwrap_or_default(),
160 auth: self.auth.unwrap_or_default(),
161 pool,
162 bindings: RwLock::new(HashMap::new()),
163 };
164 Container {
165 inner: Arc::new(inner),
166 }
167 }
168}
169
170pub trait FromContainer: Sized {
172 fn from_container(container: &Container) -> Self;
173}
174
175impl FromContainer for Container {
176 fn from_container(container: &Container) -> Self {
177 container.clone()
178 }
179}
180
181impl FromContainer for Pool {
182 fn from_container(container: &Container) -> Self {
183 container.pool().clone()
184 }
185}
186
187tokio::task_local! {
188 static CURRENT_CONTAINER: Container;
189}
190
191pub async fn with_container<F, T>(container: Container, fut: F) -> T
194where
195 F: std::future::Future<Output = T>,
196{
197 CURRENT_CONTAINER.scope(container, fut).await
198}
199
200pub fn current() -> Container {
203 CURRENT_CONTAINER
204 .try_with(|c| c.clone())
205 .expect("container not installed in current task — call inside with_container scope")
206}
207
208pub fn try_current() -> Option<Container> {
209 CURRENT_CONTAINER.try_with(|c| c.clone()).ok()
210}