Skip to main content

blackbox_core/
app.rs

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    /// Retrieves an instance of either injectable, otherwise, throws error.
102    pub fn get<TInjectable: CastFrom>(&self) -> Result<Ref<TInjectable>, String> {
103        self.get_by_token(&get_token::<TInjectable>())
104    }
105
106    /// Retrieves an instance of either injectable by token, otherwise, throws error.
107    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        // skip core modules
187        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            // static context
214            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}