torrust_tracker_deployer_lib/presentation/cli/error.rs
1//! Error Handling Module - Presentation Layer
2//!
3//! This module provides error handling functionality for the presentation layer,
4//! specifically focusing on displaying errors to users in a helpful and actionable way.
5//!
6//! ## Purpose
7//!
8//! The error handling module is responsible for:
9//! - **User-Friendly Error Display**: Converting internal errors to readable messages
10//! - **Actionable Guidance**: Providing specific steps users can take to resolve issues
11//! - **Fallback Handling**: Ensuring error messages are displayed even in degraded states
12//! - **Consistent Formatting**: Maintaining consistent error output across all commands
13//!
14//! ## Design Principles
15//!
16//! - **Observability**: All errors include sufficient context for debugging
17//! - **Actionability**: Error messages tell users how to fix problems
18//! - **Reliability**: Error handling itself must not fail
19//! - **Consistency**: All errors follow the same display patterns
20//!
21//! ## Module Integration
22//!
23//! This module integrates with:
24//! - **`CommandError` Types** - Uses structured error types from `presentation::cli::errors`
25//! - **`UserOutput` Service** - Leverages user output for consistent formatting
26//! - **Help System** - Displays detailed troubleshooting via `.help()` method
27//!
28//! ## Usage
29//!
30//! ```rust
31//! use std::sync::Arc;
32//! use std::cell::RefCell;
33//! use parking_lot::ReentrantMutex;
34//! use torrust_tracker_deployer_lib::presentation::cli::error;
35//! use torrust_tracker_deployer_lib::presentation::cli::errors::CommandError;
36//! use torrust_tracker_deployer_lib::presentation::cli::views;
37//!
38//! # fn example(error: CommandError, user_output: Arc<ReentrantMutex<RefCell<views::UserOutput>>>) {
39//! // Display error with detailed troubleshooting
40//! error::handle_error(&error, &user_output);
41//! # }
42//! ```
43
44use std::cell::RefCell;
45use std::sync::Arc;
46
47use parking_lot::ReentrantMutex;
48
49use crate::presentation::cli::errors::CommandError;
50use crate::presentation::cli::views::UserOutput;
51
52/// Handle command errors with consistent user output
53///
54/// This function provides standardized error output for all command failures.
55/// It displays the error message and detailed troubleshooting information
56/// to help users resolve issues.
57///
58/// # Arguments
59///
60/// * `error` - The command error to handle and display
61/// * `user_output` - Shared user output service for consistent output formatting
62///
63/// # Example
64///
65/// ```rust
66/// use clap::Parser;
67/// use std::sync::Arc;
68/// use std::cell::RefCell;
69/// use parking_lot::ReentrantMutex;
70/// use torrust_tracker_deployer_lib::presentation::cli::{error, errors, views};
71/// use torrust_tracker_deployer_lib::presentation::cli::controllers::destroy::DestroySubcommandError;
72/// use torrust_tracker_deployer_lib::domain::environment::name::EnvironmentNameError;
73///
74/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
75/// // Example of handling a command error (simulated for testing)
76/// let name_error = EnvironmentNameError::InvalidFormat {
77/// attempted_name: "invalid_name".to_string(),
78/// reason: "contains invalid characters: _".to_string(),
79/// valid_examples: vec!["dev".to_string(), "staging".to_string()],
80/// };
81/// let sample_error = errors::CommandError::Destroy(
82/// Box::new(DestroySubcommandError::InvalidEnvironmentName {
83/// name: "invalid_name".to_string(),
84/// source: name_error,
85/// })
86/// );
87/// let user_output = Arc::new(ReentrantMutex::new(RefCell::new(views::UserOutput::new(views::VerbosityLevel::Normal))));
88/// error::handle_error(&sample_error, &user_output);
89/// # Ok(())
90/// # }
91/// ```
92pub fn handle_error(error: &CommandError, user_output: &Arc<ReentrantMutex<RefCell<UserOutput>>>) {
93 let help_text = error.help();
94
95 // With ReentrantMutex, we can safely acquire the lock multiple times from the same thread
96 let lock = user_output.lock();
97 let mut output = lock.borrow_mut();
98 output.error(&format!("{error}"));
99 output.blank_line();
100 output.info_block("For detailed troubleshooting:", &[&help_text]);
101}