1use std::collections::HashMap;
2
3use blackbox_cast::CastFrom;
4
5use crate::{
6 builder::Builder,
7 cell::RefMut,
8 compiler::ModuleCompiler,
9 container::Container,
10 events::{OnModuleDestroy, OnModuleInit},
11 instance_wrapper::{InstanceToken, InstanceWrapper},
12 module::{Module, ModuleId},
13 modules::CoreModule,
14 reference::Ref,
15 tokens::get_token,
16 ILogger, Logger,
17};
18
19#[derive(Clone)]
20pub struct InstanceLink {
21 pub wrapper_ref: RefMut<InstanceWrapper>,
22 pub module_id: ModuleId,
23}
24
25impl InstanceLink {
26 pub fn new(module_id: ModuleId, wrapper_ref: RefMut<InstanceWrapper>) -> InstanceLink {
27 InstanceLink {
28 module_id,
29 wrapper_ref,
30 }
31 }
32}
33
34pub struct InstanceLinksHost {
35 container: RefMut<Container>,
36 instance_links: HashMap<InstanceToken, Vec<InstanceLink>>,
37}
38
39impl InstanceLinksHost {
40 pub fn new(container: RefMut<Container>) -> InstanceLinksHost {
41 InstanceLinksHost {
42 container,
43 instance_links: HashMap::new(),
44 }
45 }
46
47 pub fn init(&mut self) {
48 let modules = self.container.as_ref().get_modules();
49
50 for (_token, module) in modules {
51 let providers = module.as_ref().get_providers();
52
53 for (_token, provider) in providers {
54 self.add_link(module.clone(), provider.clone());
55 }
56 }
57 }
58
59 fn add_link(&mut self, module_ref: RefMut<Module>, wrapper_ref: RefMut<InstanceWrapper>) {
60 let token = wrapper_ref.as_ref().get_token();
61
62 let instance_link = InstanceLink::new(module_ref.as_ref().get_id(), wrapper_ref.clone());
63
64 if let Some(existing_links) = self.instance_links.get_mut(&token) {
65 existing_links.push(instance_link);
66 } else {
67 self.instance_links.insert(token, vec![instance_link]);
68 }
69 }
70
71 pub fn get_by_token(&self, token: &InstanceToken) -> Option<Vec<InstanceLink>> {
72 if let Some(instance_links) = self.instance_links.get(token) {
73 Some(instance_links.clone())
74 } else {
75 None
76 }
77 }
78}
79
80pub struct BlackBoxApp {
81 container: RefMut<Container>,
82 instance_links_host: RefMut<InstanceLinksHost>,
83}
84
85impl BlackBoxApp {
86 pub fn new(container: RefMut<Container>) -> BlackBoxApp {
87 let instance_links_host = RefMut::new(InstanceLinksHost::new(container.clone()));
88
89 instance_links_host.as_mut().init();
90
91 BlackBoxApp {
92 container,
93 instance_links_host,
94 }
95 }
96
97 pub fn get_container(&self) -> RefMut<Container> {
98 self.container.clone()
99 }
100
101 pub fn get<TInjectable: CastFrom>(&self) -> Result<Ref<TInjectable>, String> {
103 self.get_by_token(&get_token::<TInjectable>())
104 }
105
106 pub fn get_by_token<TInjectable: ?Sized + CastFrom>(
108 &self,
109 token: &InstanceToken,
110 ) -> Result<Ref<TInjectable>, String> {
111 let instance_links = self
112 .instance_links_host
113 .as_ref()
114 .get_by_token(token)
115 .expect(format!("InstanceLinksHost: {} provider was not found.", token).as_str());
116
117 for instance_link in instance_links {
118 let wrapper = instance_link.wrapper_ref.clone();
119
120 return Ok(wrapper
121 .as_ref()
122 .get_instance()
123 .unwrap()
124 .cast::<TInjectable>()
125 .unwrap());
126 }
127
128 return Err(format!(
129 "InstanceLinksHost: {} provider was not found.",
130 token
131 ));
132 }
133
134 pub fn use_logger(&self, logger: Ref<dyn ILogger>) {
135 let default_logger = self.get::<Logger>().unwrap();
136
137 default_logger.as_ref().register_logger(logger);
138 default_logger.as_ref().flush();
139 }
140}
141
142pub struct BuildParams {
143 buffer_logs: bool,
144}
145
146impl BuildParams {
147 pub fn default() -> BuildParams {
148 BuildParams { buffer_logs: false }
149 }
150
151 pub fn buffer_logs(mut self) -> Self {
152 self.buffer_logs = true;
153
154 return self;
155 }
156}
157
158pub async fn build<TModule: ModuleCompiler>(params: BuildParams) -> Ref<BlackBoxApp> {
159 let builder = RefMut::new(Builder::new());
160
161 let core_module_builder = builder.as_mut().register_module::<CoreModule>();
162 CoreModule::__blackbox_build(core_module_builder);
163
164 let root_module_builder = builder.as_mut().register_module::<TModule>();
165 TModule::__blackbox_build(root_module_builder);
166
167 init(builder.clone());
168 link(builder.clone());
169
170 let app = builder.as_ref().build();
171
172 let container = app.as_ref().get_container();
173 let mut modules = container.as_ref().get_modules_sorted_by_distance();
174
175 modules.reverse();
176
177 let logger = app.get::<Logger>().unwrap();
178
179 if params.buffer_logs {
180 logger.attach_buffer();
181 }
182
183 for module in modules {
184 let token = module.as_ref().get_token();
185
186 if token.starts_with("blackbox_core::") {
188 continue;
189 }
190
191 logger.info_with_ctx(
192 format!("{} dependencies initialized", token).as_str(),
193 "ModuleLoader",
194 )
195 }
196
197 listen_exit_signal(app.clone());
198
199 call_init_hook(app.clone()).await;
200
201 return app;
202}
203
204fn init(builder: RefMut<Builder>) {
205 let module_builders = builder.as_ref().get_modules();
206
207 for (_token, module_builder) in module_builders {
208 let providers = module_builder.providers.as_ref().clone();
209
210 for (token, provider_builder) in providers {
211 let dep_tokens = provider_builder.dep_init_fns.clone();
212
213 provider_builder.register_instance_by_token(&token);
215
216 for (token, _) in dep_tokens.as_ref().iter() {
217 provider_builder.register_instance_by_token(token);
218 }
219 }
220 }
221}
222
223fn link(builder: RefMut<Builder>) {
224 let module_builders = builder.as_ref().get_modules();
225
226 for (_token, module_builder) in module_builders {
227 let providers = module_builder.providers.as_ref().clone();
228
229 for (_token, provider_builder) in providers {
230 let dep_tokens = provider_builder.dep_init_fns.clone();
231
232 let instances = provider_builder.instance_wrapper.as_ref().get_instances();
233
234 for instance in instances {
235 for (token, _) in dep_tokens.as_ref().iter() {
236 provider_builder.link_instance_by_token(token, instance.clone());
237 }
238 }
239 }
240 }
241}
242
243async fn call_init_hook(app: Ref<BlackBoxApp>) {
244 let modules = app
245 .get_container()
246 .as_ref()
247 .get_modules_sorted_by_distance();
248
249 for module in modules {
250 let providers = module.as_ref().get_providers();
251
252 for (_token, provider) in providers {
253 let instances = provider.as_ref().get_instances();
254
255 for instance in instances {
256 if let Ok(provider) = instance.cast::<dyn OnModuleInit>() {
257 provider.as_ref().on_module_init().await;
258 }
259 }
260 }
261 }
262}
263
264async fn call_destroy_hook(app: Ref<BlackBoxApp>) {
265 let modules = app
266 .get_container()
267 .as_ref()
268 .get_modules_sorted_by_distance();
269
270 for module in modules {
271 let providers = module.as_ref().get_providers();
272
273 for (_token, provider) in providers {
274 let instances = provider.as_ref().get_instances();
275
276 for instance in instances {
277 if let Ok(provider) = instance.cast::<dyn OnModuleDestroy>() {
278 provider.as_ref().on_module_destroy().await;
279 }
280 }
281 }
282 }
283}
284
285fn listen_exit_signal(app: Ref<BlackBoxApp>) {
286 tokio::spawn(async move {
287 tokio::signal::ctrl_c().await.unwrap();
288
289 call_destroy_hook(app).await;
290 });
291}