Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/bootstrap/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ use crate::{bootstrap, presentation};
/// This function serves as the application bootstrap, handling:
/// 1. CLI argument parsing (delegated to presentation layer)
/// 2. Logging initialization using `LoggingConfig`
/// 3. Command execution (delegated to presentation layer)
/// 4. Error handling and exit code management
/// 3. Service container creation for dependency injection
/// 4. Command execution (delegated to presentation layer)
/// 5. Error handling and exit code management
///
/// # Panics
///
Expand All @@ -54,10 +55,15 @@ pub fn run() {
"Application started"
);

// Initialize service container for dependency injection
let container = bootstrap::Container::new();

match cli.command {
Some(command) => {
if let Err(e) = presentation::execute(command, &cli.global.working_dir) {
presentation::handle_error(&e);
if let Err(e) =
presentation::execute(command, &cli.global.working_dir, &container.user_output())
{
presentation::handle_error(&e, &container.user_output());
std::process::exit(1);
}
}
Expand Down
103 changes: 103 additions & 0 deletions src/bootstrap/container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//! Application Service Container
//!
//! This module provides centralized initialization of application-wide services
//! that need consistent configuration across the entire application.

use std::sync::{Arc, Mutex};

use crate::presentation::commands::constants::DEFAULT_VERBOSITY;
use crate::presentation::user_output::UserOutput;

/// Application service container
///
/// Holds shared services initialized during application bootstrap.
/// Services are wrapped in `Arc<Mutex<T>>` for thread-safe shared ownership
/// with interior mutability across the application.
///
/// # Example
///
/// ```rust
/// use torrust_tracker_deployer_lib::bootstrap::container::Container;
///
/// let container = Container::new();
/// let user_output = container.user_output();
/// user_output.lock().unwrap().success("Operation completed");
/// ```
#[derive(Clone)]
pub struct Container {
user_output: Arc<Mutex<UserOutput>>,
}

impl Container {
/// Create a new container with initialized services
///
/// Uses `DEFAULT_VERBOSITY` for user output. In the future, this may
/// accept a verbosity parameter from CLI flags.
#[must_use]
pub fn new() -> Self {
let user_output = Arc::new(Mutex::new(UserOutput::new(DEFAULT_VERBOSITY)));

Self { user_output }
}

/// Get shared reference to user output service
///
/// Returns an `Arc<Mutex<UserOutput>>` that can be cheaply cloned and shared
/// across threads and function calls. Lock the mutex to access the user output.
///
/// # Example
///
/// ```rust
/// use torrust_tracker_deployer_lib::bootstrap::container::Container;
///
/// let container = Container::new();
/// let user_output = container.user_output();
/// user_output.lock().unwrap().success("Operation completed");
/// ```
#[must_use]
pub fn user_output(&self) -> Arc<Mutex<UserOutput>> {
Arc::clone(&self.user_output)
}
}

impl Default for Container {
fn default() -> Self {
Self::new()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_should_create_container_with_user_output() {
let container = Container::new();
let user_output = container.user_output();

// Verify we can get the user_output service
assert!(Arc::strong_count(&user_output) >= 1);
}

#[test]
fn it_should_return_cloned_arc_on_user_output_access() {
let container = Container::new();
let user_output1 = container.user_output();
let user_output2 = container.user_output();

// Both should point to the same UserOutput instance
assert!(Arc::ptr_eq(&user_output1, &user_output2));
}

#[test]
fn it_should_be_clonable() {
let container1 = Container::new();
let container2 = container1.clone();

let user_output1 = container1.user_output();
let user_output2 = container2.user_output();

// Cloned containers should share the same UserOutput
assert!(Arc::ptr_eq(&user_output1, &user_output2));
}
}
5 changes: 4 additions & 1 deletion src/bootstrap/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
//! Bootstrap Module
//!
//! This module contains application initialization and bootstrap concerns.
//! It handles application lifecycle, logging setup, and help display.
//! It handles application lifecycle, logging setup, help display, and service container.
//!
//! ## Modules
//!
//! - `app` - Main application bootstrap and entry point logic
//! - `container` - Application service container for dependency injection
//! - `help` - Help and usage information display
//! - `logging` - Logging configuration and initialization

pub mod app;
pub mod container;
pub mod help;
pub mod logging;

// Re-export commonly used types for convenience
pub use container::Container;
pub use logging::{LogFormat, LogOutput, LoggingBuilder, LoggingConfig};
Loading