torrust_tracker_deployer_lib/bootstrap/
container.rs1use std::cell::RefCell;
7use std::path::Path;
8use std::sync::Arc;
9
10use parking_lot::ReentrantMutex;
11
12use crate::application::command_handlers::PurgeCommandHandler;
13use crate::application::traits::RepositoryProvider;
14use crate::domain::environment::repository::EnvironmentRepository;
15use crate::infrastructure::persistence::file_repository_factory::FileRepositoryFactory;
16use crate::presentation::cli::controllers::configure::ConfigureCommandController;
17use crate::presentation::cli::controllers::constants::DEFAULT_LOCK_TIMEOUT;
18use crate::presentation::cli::controllers::create::subcommands::environment::CreateEnvironmentCommandController;
19use crate::presentation::cli::controllers::create::subcommands::schema::CreateSchemaCommandController;
20use crate::presentation::cli::controllers::create::subcommands::template::CreateTemplateCommandController;
21use crate::presentation::cli::controllers::destroy::DestroyCommandController;
22use crate::presentation::cli::controllers::docs::DocsCommandController;
23use crate::presentation::cli::controllers::exists::ExistsCommandController;
24use crate::presentation::cli::controllers::list::ListCommandController;
25use crate::presentation::cli::controllers::provision::ProvisionCommandController;
26use crate::presentation::cli::controllers::purge::PurgeCommandController;
27use crate::presentation::cli::controllers::register::RegisterCommandController;
28use crate::presentation::cli::controllers::release::ReleaseCommandController;
29use crate::presentation::cli::controllers::render::RenderCommandController;
30use crate::presentation::cli::controllers::run::RunCommandController;
31use crate::presentation::cli::controllers::show::ShowCommandController;
32use crate::presentation::cli::controllers::test::handler::TestCommandController;
33use crate::presentation::cli::controllers::validate::ValidateCommandController;
34use crate::presentation::cli::views::{UserOutput, VerbosityLevel};
35use crate::shared::clock::Clock;
36use crate::shared::SystemClock;
37
38#[derive(Clone)]
57pub struct Container {
58 user_output: Arc<ReentrantMutex<RefCell<UserOutput>>>,
59 file_repository_factory: Arc<FileRepositoryFactory>,
60 repository: Arc<dyn EnvironmentRepository + Send + Sync>,
61 clock: Arc<dyn Clock>,
62 data_directory: Arc<Path>,
63}
64
65impl Container {
66 #[must_use]
93 pub fn new(verbosity_level: VerbosityLevel, working_dir: &Path) -> Self {
94 let user_output = Arc::new(ReentrantMutex::new(RefCell::new(UserOutput::new(
95 verbosity_level,
96 ))));
97 let file_repository_factory = Arc::new(FileRepositoryFactory::new(DEFAULT_LOCK_TIMEOUT));
98
99 let data_dir = working_dir.join("data");
101 let data_directory: Arc<Path> = Arc::from(data_dir.as_path());
102 let repository = file_repository_factory.create(data_dir);
103
104 let clock: Arc<dyn Clock> = Arc::new(SystemClock);
105
106 Self {
107 user_output,
108 file_repository_factory,
109 repository,
110 clock,
111 data_directory,
112 }
113 }
114
115 #[must_use]
134 pub fn user_output(&self) -> Arc<ReentrantMutex<RefCell<UserOutput>>> {
135 Arc::clone(&self.user_output)
136 }
137
138 #[must_use]
155 pub fn file_repository_factory(&self) -> Arc<FileRepositoryFactory> {
156 Arc::clone(&self.file_repository_factory)
157 }
158
159 #[must_use]
164 pub fn repository_provider(&self) -> Arc<dyn RepositoryProvider> {
165 Arc::clone(&self.file_repository_factory) as Arc<dyn RepositoryProvider>
166 }
167
168 #[must_use]
186 pub fn repository(&self) -> Arc<dyn EnvironmentRepository + Send + Sync> {
187 Arc::clone(&self.repository)
188 }
189
190 #[must_use]
207 pub fn clock(&self) -> Arc<dyn Clock> {
208 Arc::clone(&self.clock)
209 }
210
211 #[must_use]
213 pub fn create_environment_controller(&self) -> CreateEnvironmentCommandController {
214 CreateEnvironmentCommandController::new(
215 self.repository(),
216 self.clock(),
217 &self.user_output(),
218 )
219 }
220
221 #[must_use]
223 pub fn create_template_controller(&self) -> CreateTemplateCommandController {
224 CreateTemplateCommandController::new(&self.user_output())
225 }
226
227 #[must_use]
229 pub fn create_schema_controller(&self) -> CreateSchemaCommandController {
230 CreateSchemaCommandController::new(&self.user_output())
231 }
232
233 #[must_use]
235 pub fn create_docs_controller(&self) -> DocsCommandController {
236 DocsCommandController::new(&self.user_output())
237 }
238
239 #[must_use]
241 pub fn create_provision_controller(&self) -> ProvisionCommandController {
242 ProvisionCommandController::new(self.repository(), self.clock(), self.user_output())
243 }
244
245 #[must_use]
247 pub fn create_destroy_controller(&self) -> DestroyCommandController {
248 DestroyCommandController::new(self.repository(), self.clock(), self.user_output())
249 }
250
251 #[must_use]
253 pub fn create_purge_controller(&self) -> PurgeCommandController {
254 let handler =
255 PurgeCommandHandler::new(self.repository(), (*self.data_directory).to_path_buf());
256 PurgeCommandController::new(handler, self.user_output())
257 }
258
259 #[must_use]
261 pub fn create_configure_controller(&self) -> ConfigureCommandController {
262 ConfigureCommandController::new(self.repository(), self.clock(), self.user_output())
263 }
264
265 #[must_use]
267 pub fn create_test_controller(&self) -> TestCommandController {
268 TestCommandController::new(self.repository(), self.user_output())
269 }
270
271 #[must_use]
273 pub fn create_validate_controller(&self) -> ValidateCommandController {
274 ValidateCommandController::new(self.user_output())
275 }
276
277 #[must_use]
279 pub fn create_register_controller(&self) -> RegisterCommandController {
280 RegisterCommandController::new(self.repository(), self.clock(), self.user_output())
281 }
282
283 #[must_use]
285 pub fn create_release_controller(&self) -> ReleaseCommandController {
286 ReleaseCommandController::new(self.repository(), self.clock(), self.user_output())
287 }
288
289 #[must_use]
291 pub fn create_render_controller(&self) -> RenderCommandController {
292 RenderCommandController::new(self.repository(), self.user_output())
293 }
294
295 #[must_use]
297 pub fn create_run_controller(&self) -> RunCommandController {
298 RunCommandController::new(self.repository(), self.clock(), self.user_output())
299 }
300
301 #[must_use]
303 pub fn create_show_controller(&self) -> ShowCommandController {
304 ShowCommandController::new(self.repository(), self.user_output())
305 }
306
307 #[must_use]
309 pub fn create_exists_controller(&self) -> ExistsCommandController {
310 ExistsCommandController::new(self.repository(), self.user_output())
311 }
312
313 #[must_use]
315 pub fn create_list_controller(&self) -> ListCommandController {
316 ListCommandController::new(
317 self.repository_provider(),
318 self.data_directory(),
319 self.user_output(),
320 )
321 }
322
323 #[must_use]
328 pub fn data_directory(&self) -> Arc<Path> {
329 Arc::clone(&self.data_directory)
330 }
331}
332
333impl Default for Container {
334 fn default() -> Self {
335 Self::new(VerbosityLevel::Normal, Path::new("."))
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use tempfile::TempDir;
343
344 #[test]
345 fn it_should_create_container_with_all_services() {
346 let temp_dir = TempDir::new().unwrap();
347 let container = Container::new(VerbosityLevel::Normal, temp_dir.path());
348
349 let user_output = container.user_output();
351 let file_repository_factory = container.file_repository_factory();
352 let repository = container.repository();
353 let clock = container.clock();
354
355 assert!(Arc::strong_count(&user_output) >= 1);
356 assert!(Arc::strong_count(&file_repository_factory) >= 1);
357 assert!(Arc::strong_count(&repository) >= 1);
358 assert!(Arc::strong_count(&clock) >= 1);
359 }
360
361 #[test]
362 fn it_should_return_cloned_arc_on_file_repository_factory_access() {
363 let temp_dir = TempDir::new().unwrap();
364 let container = Container::new(VerbosityLevel::Normal, temp_dir.path());
365 let factory1 = container.file_repository_factory();
366 let factory2 = container.file_repository_factory();
367
368 assert!(Arc::ptr_eq(&factory1, &factory2));
370 }
371
372 #[test]
373 fn it_should_return_cloned_arc_on_repository_access() {
374 let temp_dir = TempDir::new().unwrap();
375 let container = Container::new(VerbosityLevel::Normal, temp_dir.path());
376 let repo1 = container.repository();
377 let repo2 = container.repository();
378
379 assert!(Arc::ptr_eq(&repo1, &repo2));
381 }
382
383 #[test]
384 fn it_should_return_cloned_arc_on_clock_access() {
385 let temp_dir = TempDir::new().unwrap();
386 let container = Container::new(VerbosityLevel::Normal, temp_dir.path());
387 let clock1 = container.clock();
388 let clock2 = container.clock();
389
390 assert!(Arc::ptr_eq(&clock1, &clock2));
392 }
393
394 #[test]
395 fn it_should_return_cloned_arc_on_user_output_access() {
396 let temp_dir = TempDir::new().unwrap();
397 let container = Container::new(VerbosityLevel::Normal, temp_dir.path());
398 let user_output1 = container.user_output();
399 let user_output2 = container.user_output();
400
401 assert!(Arc::ptr_eq(&user_output1, &user_output2));
403 }
404
405 #[test]
406 fn it_should_be_clonable() {
407 let temp_dir = TempDir::new().unwrap();
408 let container1 = Container::new(VerbosityLevel::Normal, temp_dir.path());
409 let container2 = container1.clone();
410
411 let user_output1 = container1.user_output();
413 let user_output2 = container2.user_output();
414 assert!(Arc::ptr_eq(&user_output1, &user_output2));
415
416 let factory1 = container1.file_repository_factory();
417 let factory2 = container2.file_repository_factory();
418 assert!(Arc::ptr_eq(&factory1, &factory2));
419
420 let repo1 = container1.repository();
421 let repo2 = container2.repository();
422 assert!(Arc::ptr_eq(&repo1, &repo2));
423
424 let clock1 = container1.clock();
425 let clock2 = container2.clock();
426 assert!(Arc::ptr_eq(&clock1, &clock2));
427 }
428
429 #[test]
430 fn it_should_create_container_with_silent_verbosity_for_tests() {
431 let temp_dir = TempDir::new().unwrap();
432 let container = Container::new(VerbosityLevel::Silent, temp_dir.path());
433
434 let user_output = container.user_output();
436 let file_repository_factory = container.file_repository_factory();
437 let repository = container.repository();
438 let clock = container.clock();
439
440 assert!(Arc::strong_count(&user_output) >= 1);
441 assert!(Arc::strong_count(&file_repository_factory) >= 1);
442 assert!(Arc::strong_count(&repository) >= 1);
443 assert!(Arc::strong_count(&clock) >= 1);
444
445 }
448
449 #[test]
450 fn it_should_create_container_with_different_verbosity_levels() {
451 let temp_dir = TempDir::new().unwrap();
452
453 let levels = [
455 VerbosityLevel::Silent,
456 VerbosityLevel::Quiet,
457 VerbosityLevel::Normal,
458 VerbosityLevel::Verbose,
459 VerbosityLevel::VeryVerbose,
460 VerbosityLevel::Debug,
461 ];
462
463 for level in &levels {
464 let container = Container::new(*level, temp_dir.path());
465
466 let user_output = container.user_output();
468 let file_repository_factory = container.file_repository_factory();
469 let repository = container.repository();
470 let clock = container.clock();
471
472 assert!(Arc::strong_count(&user_output) >= 1);
473 assert!(Arc::strong_count(&file_repository_factory) >= 1);
474 assert!(Arc::strong_count(&repository) >= 1);
475 assert!(Arc::strong_count(&clock) >= 1);
476 }
477 }
478}