1use crate::{BootError, BoxFuture, Module, ModuleRef, ProviderToken, Result};
2use std::collections::BTreeMap;
3use std::fmt;
4use std::sync::{Arc, RwLock};
5
6#[derive(Clone)]
8pub struct LazyLoadedModule {
9 name: String,
10 module_ref: ModuleRef,
11 exports: ModuleRef,
12}
13
14impl LazyLoadedModule {
15 fn new(name: String, module_ref: ModuleRef, exports: ModuleRef) -> Self {
16 Self {
17 name,
18 module_ref,
19 exports,
20 }
21 }
22
23 pub fn name(&self) -> &str {
25 &self.name
26 }
27
28 pub fn module_ref(&self) -> &ModuleRef {
30 &self.module_ref
31 }
32
33 pub fn get<T>(&self) -> Result<Arc<T>>
35 where
36 T: Send + Sync + 'static,
37 {
38 self.module_ref.get::<T>()
39 }
40
41 pub fn get_named<T>(&self, token: &str) -> Result<Arc<T>>
43 where
44 T: Send + Sync + 'static,
45 {
46 self.module_ref.get_named::<T>(token)
47 }
48
49 pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>>
51 where
52 T: Send + Sync + 'static,
53 {
54 self.module_ref.get_optional::<T>()
55 }
56
57 pub fn get_optional_named<T>(&self, token: &str) -> Result<Option<Arc<T>>>
59 where
60 T: Send + Sync + 'static,
61 {
62 self.module_ref.get_optional_named::<T>(token)
63 }
64}
65
66impl fmt::Debug for LazyLoadedModule {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.debug_struct("LazyLoadedModule")
69 .field("name", &self.name)
70 .field("module_ref", &self.module_ref)
71 .finish()
72 }
73}
74
75#[derive(Clone)]
77pub struct LazyModuleLoader {
78 registry: Arc<LazyModuleRegistry>,
79}
80
81impl LazyModuleLoader {
82 pub(crate) fn new(global_ref: ModuleRef) -> Self {
83 Self {
84 registry: Arc::new(LazyModuleRegistry::new(global_ref)),
85 }
86 }
87
88 pub(crate) fn seed_module(
89 &self,
90 name: String,
91 module_ref: ModuleRef,
92 exports: ModuleRef,
93 ) -> Result<()> {
94 self.registry
95 .seed_module(LazyLoadedModule::new(name, module_ref, exports))
96 }
97
98 pub fn load<M>(&self, module: M) -> Result<LazyLoadedModule>
103 where
104 M: Module,
105 {
106 self.load_arc(Arc::new(module))
107 }
108
109 pub fn load_arc(&self, module: Arc<dyn Module>) -> Result<LazyLoadedModule> {
111 let mut visiting = Vec::new();
112 self.load_arc_inner(module, &mut visiting)
113 }
114
115 pub async fn load_async<M>(&self, module: M) -> Result<LazyLoadedModule>
117 where
118 M: Module,
119 {
120 self.load_arc_async(Arc::new(module)).await
121 }
122
123 pub async fn load_arc_async(&self, module: Arc<dyn Module>) -> Result<LazyLoadedModule> {
125 let mut visiting = Vec::new();
126 self.load_arc_async_inner(module, &mut visiting).await
127 }
128
129 fn load_arc_inner(
130 &self,
131 module: Arc<dyn Module>,
132 visiting: &mut Vec<String>,
133 ) -> Result<LazyLoadedModule> {
134 let name = validate_lazy_module_name(module.name())?;
135 if let Some(cached) = self.registry.cached(name)? {
136 return Ok(cached);
137 }
138
139 enter_lazy_module(visiting, name)?;
140 let result = self.build_lazy_module(module, name, visiting);
141 visiting.pop();
142 result
143 }
144
145 fn build_lazy_module(
146 &self,
147 module: Arc<dyn Module>,
148 name: &str,
149 visiting: &mut Vec<String>,
150 ) -> Result<LazyLoadedModule> {
151 let mut imported_modules = Vec::new();
152 for imported in module.imports() {
153 imported_modules.push(self.load_arc_inner(imported, visiting)?);
154 }
155
156 let module_ref = self.create_module_ref(&imported_modules)?;
157 for provider in module.providers()? {
158 module_ref.register(provider)?;
159 }
160 module_ref.initialize_local_singletons()?;
161
162 let exports = self.create_exports(&module_ref, module.exports()?)?;
163 if module.is_global() {
164 self.export_global(&exports)?;
165 }
166
167 let loaded = LazyLoadedModule::new(name.to_string(), module_ref, exports);
168 self.registry.cache_module(loaded)
169 }
170
171 fn load_arc_async_inner<'a>(
172 &'a self,
173 module: Arc<dyn Module>,
174 visiting: &'a mut Vec<String>,
175 ) -> BoxFuture<'a, Result<LazyLoadedModule>> {
176 Box::pin(async move {
177 let name = validate_lazy_module_name(module.name())?;
178 if let Some(cached) = self.registry.cached(name)? {
179 return Ok(cached);
180 }
181
182 enter_lazy_module(visiting, name)?;
183 let result = self.build_lazy_module_async(module, name, visiting).await;
184 visiting.pop();
185 result
186 })
187 }
188
189 fn build_lazy_module_async<'a>(
190 &'a self,
191 module: Arc<dyn Module>,
192 name: &'a str,
193 visiting: &'a mut Vec<String>,
194 ) -> BoxFuture<'a, Result<LazyLoadedModule>> {
195 Box::pin(async move {
196 let mut imported_modules = Vec::new();
197 for imported in module.imports() {
198 imported_modules.push(self.load_arc_async_inner(imported, visiting).await?);
199 }
200
201 let module_ref = self.create_module_ref(&imported_modules)?;
202 for provider in module.providers()? {
203 module_ref.register_async(provider).await?;
204 }
205 module_ref.initialize_local_singletons_async().await?;
206
207 let exports = self.create_exports(&module_ref, module.exports()?)?;
208 if module.is_global() {
209 self.export_global(&exports)?;
210 }
211
212 let loaded = LazyLoadedModule::new(name.to_string(), module_ref, exports);
213 self.registry.cache_module(loaded)
214 })
215 }
216
217 fn create_module_ref(&self, imported_modules: &[LazyLoadedModule]) -> Result<ModuleRef> {
218 let module_ref = ModuleRef::new();
219 module_ref.add_visible_scope(self.registry.global_ref.clone())?;
220 for imported in imported_modules {
221 module_ref.add_visible_scope(imported.exports.clone())?;
222 }
223 Ok(module_ref)
224 }
225
226 fn create_exports(
227 &self,
228 module_ref: &ModuleRef,
229 tokens: Vec<ProviderToken>,
230 ) -> Result<ModuleRef> {
231 let exports = ModuleRef::new();
232 for token in tokens {
233 exports.export_from(module_ref, &token)?;
234 }
235 Ok(exports)
236 }
237
238 fn export_global(&self, exports: &ModuleRef) -> Result<()> {
239 for token in exports.local_tokens()? {
240 self.registry.global_ref.export_from(exports, &token)?;
241 }
242 Ok(())
243 }
244}
245
246impl fmt::Debug for LazyModuleLoader {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 f.debug_struct("LazyModuleLoader").finish_non_exhaustive()
249 }
250}
251
252struct LazyModuleRegistry {
253 global_ref: ModuleRef,
254 modules: RwLock<BTreeMap<String, LazyLoadedModule>>,
255}
256
257impl LazyModuleRegistry {
258 fn new(global_ref: ModuleRef) -> Self {
259 Self {
260 global_ref,
261 modules: RwLock::new(BTreeMap::new()),
262 }
263 }
264
265 fn cached(&self, name: &str) -> Result<Option<LazyLoadedModule>> {
266 Ok(self.read_modules()?.get(name).cloned())
267 }
268
269 fn seed_module(&self, module: LazyLoadedModule) -> Result<()> {
270 self.write_modules()?.insert(module.name.clone(), module);
271 Ok(())
272 }
273
274 fn cache_module(&self, module: LazyLoadedModule) -> Result<LazyLoadedModule> {
275 let mut modules = self.write_modules()?;
276 if let Some(cached) = modules.get(module.name()).cloned() {
277 return Ok(cached);
278 }
279 modules.insert(module.name.clone(), module.clone());
280 Ok(module)
281 }
282
283 fn read_modules(
284 &self,
285 ) -> Result<std::sync::RwLockReadGuard<'_, BTreeMap<String, LazyLoadedModule>>> {
286 self.modules
287 .read()
288 .map_err(|_| BootError::Internal("lazy module registry lock is poisoned".to_string()))
289 }
290
291 fn write_modules(
292 &self,
293 ) -> Result<std::sync::RwLockWriteGuard<'_, BTreeMap<String, LazyLoadedModule>>> {
294 self.modules
295 .write()
296 .map_err(|_| BootError::Internal("lazy module registry lock is poisoned".to_string()))
297 }
298}
299
300fn validate_lazy_module_name(name: &'static str) -> Result<&'static str> {
301 if name.trim().is_empty() {
302 return Err(BootError::EmptyModuleName);
303 }
304 Ok(name)
305}
306
307fn enter_lazy_module(visiting: &mut Vec<String>, name: &str) -> Result<()> {
308 if let Some(index) = visiting.iter().position(|active| active == name) {
309 let mut chain = visiting[index..].to_vec();
310 chain.push(name.to_string());
311 return Err(BootError::Internal(format!(
312 "cyclic lazy module import detected: {}",
313 chain.join(" -> ")
314 )));
315 }
316
317 visiting.push(name.to_string());
318 Ok(())
319}