Skip to main content

torrust_tracker_deployer_lib/presentation/cli/dispatch/
router.rs

1//! Command Router
2//!
3//! This module provides the central command routing functionality for the Dispatch Layer.
4//! It contains the `route_command` function that matches parsed CLI commands to their
5//! appropriate handler functions.
6//!
7//! ## Purpose
8//!
9//! The router extracts command dispatch logic from the main application bootstrap and
10//! the presentation commands module, creating a clean separation between:
11//!
12//! - **Command parsing** (Input Layer - already done)
13//! - **Command routing** (This module - routes commands to handlers)
14//! - **Command execution** (Controller Layer - executes business logic)
15//! - **Result presentation** (View Layer - displays results)
16//!
17//! ## Design
18//!
19//! ```text
20//! Commands enum → route_command() → Handler function
21//!      ↓                ↓                    ↓
22//! Parsed input    Route decision      Business logic
23//! ```
24//!
25//! ## Benefits
26//!
27//! - **Centralized Routing**: All command routing logic in one place
28//! - **Type Safety**: Compile-time guarantees that all commands are handled
29//! - **Testability**: Router can be tested independently of handlers
30//! - **Maintainability**: Easy to add new commands or modify routing logic
31//!
32//! ## Usage Example
33//!
34//! ```rust,ignore
35//! use std::path::Path;
36//! use std::sync::Arc;
37//! use torrust_tracker_deployer_lib::bootstrap::Container;
38//! use torrust_tracker_deployer_lib::presentation::cli::dispatch::{route_command, ExecutionContext};
39//! use torrust_tracker_deployer_lib::presentation::cli::views::VerbosityLevel;
40//! // Note: Commands enum requires specific action parameters in practice
41//!
42//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
43//! let container = Container::new(VerbosityLevel::Normal);
44//! let context = ExecutionContext::new(Arc::new(container), global_args);
45//! let working_dir = Path::new(".");
46//!
47//! // Route command to appropriate handler
48//! // Note: Commands require proper construction with actions
49//! # Ok(())
50//! # }
51//! ```
52
53use std::path::Path;
54
55use crate::presentation::cli::controllers::create;
56use crate::presentation::cli::errors::CommandError;
57use crate::presentation::cli::input::Commands;
58
59use super::ExecutionContext;
60
61/// Route a parsed command to its appropriate handler
62///
63/// This function serves as the central dispatch point for all CLI commands.
64/// It takes a parsed command and an execution context, then routes the command
65/// to the appropriate handler function in the Controllers layer.
66///
67/// # Arguments
68///
69/// * `command` - Parsed command from the Input Layer
70/// * `working_dir` - Working directory for command execution
71/// * `context` - Execution context providing access to application services
72///
73/// # Returns
74///
75/// Returns `Ok(())` on successful command execution, or a `CommandError`
76/// if the command fails. The error contains detailed context and actionable
77/// troubleshooting information.
78///
79/// # Errors
80///
81/// Returns an error if:
82/// - Command handler execution fails
83/// - Required services are not available in the context
84/// - Command parameters are invalid
85///
86/// # Examples
87///
88/// ```text
89/// use std::path::Path;
90/// use std::sync::Arc;
91/// use torrust_tracker_deployer_lib::bootstrap::Container;
92/// use torrust_tracker_deployer_lib::presentation::cli::dispatch::{route_command, ExecutionContext};
93/// use torrust_tracker_deployer_lib::presentation::cli::views::VerbosityLevel;
94/// // Note: Commands enum requires specific action parameters in practice
95///
96/// async fn example() -> Result<(), Box<dyn std::error::Error>> {
97///     let container = Container::new(VerbosityLevel::Normal);
98///     let context = ExecutionContext::new(Arc::new(container), global_args);
99///     let working_dir = Path::new(".");
100///
101///     // Route command to appropriate handler - requires proper Commands construction
102///     // route_command(command, working_dir, &context).await?;
103///     Ok(())
104/// }
105/// ```
106#[allow(clippy::too_many_lines)]
107pub async fn route_command(
108    command: Commands,
109    working_dir: &Path,
110    context: &ExecutionContext,
111) -> Result<(), CommandError> {
112    match command {
113        Commands::Create { action } => {
114            create::route_command(action, working_dir, context).await?;
115            Ok(())
116        }
117        Commands::Destroy { environment } => {
118            let output_format = context.output_format();
119            context
120                .container()
121                .create_destroy_controller()
122                .execute(&environment, output_format)
123                .await?;
124            Ok(())
125        }
126        Commands::Purge { environment, force } => {
127            let output_format = context.output_format();
128            context
129                .container()
130                .create_purge_controller()
131                .execute(&environment, force, output_format)
132                .await?;
133            Ok(())
134        }
135        Commands::Provision { environment } => {
136            let output_format = context.output_format();
137            context
138                .container()
139                .create_provision_controller()
140                .execute(&environment, output_format)
141                .await?;
142            Ok(())
143        }
144        Commands::Configure { environment } => {
145            let output_format = context.output_format();
146            context
147                .container()
148                .create_configure_controller()
149                .execute(&environment, output_format)?;
150            Ok(())
151        }
152        Commands::Test { environment } => {
153            let output_format = context.output_format();
154            context
155                .container()
156                .create_test_controller()
157                .execute(&environment, output_format)
158                .await?;
159            Ok(())
160        }
161        Commands::Validate { env_file } => {
162            let output_format = context.output_format();
163            context
164                .container()
165                .create_validate_controller()
166                .execute(&env_file, output_format)?;
167            Ok(())
168        }
169        Commands::Register {
170            environment,
171            instance_ip,
172            ssh_port,
173        } => {
174            let output_format = context.output_format();
175            context
176                .container()
177                .create_register_controller()
178                .execute(&environment, &instance_ip, ssh_port, output_format)
179                .await?;
180            Ok(())
181        }
182        Commands::Release { environment } => {
183            let output_format = context.output_format();
184            context
185                .container()
186                .create_release_controller()
187                .execute(&environment, output_format)
188                .await?;
189            Ok(())
190        }
191        Commands::Render {
192            env_name,
193            env_file,
194            instance_ip,
195            output_dir,
196            force,
197        } => {
198            let output_format = context.output_format();
199            context
200                .container()
201                .create_render_controller()
202                .execute(
203                    env_name.as_deref(),
204                    env_file.as_deref(),
205                    &instance_ip,
206                    output_dir.as_path(),
207                    force,
208                    context.working_dir(),
209                    output_format,
210                )
211                .await?;
212            Ok(())
213        }
214        Commands::Run { environment } => {
215            let output_format = context.output_format();
216            context
217                .container()
218                .create_run_controller()
219                .execute(&environment, output_format)
220                .await?;
221            Ok(())
222        }
223        Commands::Show { environment } => {
224            context
225                .container()
226                .create_show_controller()
227                .execute(&environment, context.output_format())?;
228            Ok(())
229        }
230        Commands::Exists { environment } => {
231            context
232                .container()
233                .create_exists_controller()
234                .execute(&environment, context.output_format())?;
235            Ok(())
236        }
237        Commands::List => {
238            let output_format = context.output_format();
239            context
240                .container()
241                .create_list_controller()
242                .execute(output_format)?;
243            Ok(())
244        }
245        Commands::Docs { output_path } => {
246            context
247                .container()
248                .create_docs_controller()
249                .execute(output_path.as_ref())?;
250            Ok(())
251        }
252    }
253}