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}