Skip to content

Commit 921bc85

Browse files
committed
refactor: [#162] reorganize destroy handler functions by abstraction level
- Reorder functions in destroy/handler.rs: handle() → handle_destroy_command() → DestroyCommandController - Improve code readability with top-down abstraction hierarchy - Maintain all existing functionality and tests - Add clear section headers for better navigation
1 parent 4e1da6f commit 921bc85

File tree

1 file changed

+153
-157
lines changed

1 file changed

+153
-157
lines changed

src/presentation/controllers/destroy/handler.rs

Lines changed: 153 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,159 @@ use super::errors::DestroySubcommandError;
2525
const DESTROY_WORKFLOW_STEPS: usize = 3;
2626

2727
// ============================================================================
28-
// PRESENTATION LAYER CONTROLLER
28+
// HIGH-LEVEL API (EXECUTION CONTEXT PATTERN)
29+
// ============================================================================
30+
31+
/// Handle destroy command using `ExecutionContext` pattern
32+
///
33+
/// This function provides a clean interface for destroying deployment environments,
34+
/// integrating with the `ExecutionContext` pattern for dependency injection.
35+
///
36+
/// # Arguments
37+
///
38+
/// * `environment_name` - Name of the environment to destroy
39+
/// * `working_dir` - Working directory path for operations
40+
/// * `context` - Execution context providing access to services
41+
///
42+
/// # Returns
43+
///
44+
/// * `Ok(())` - Environment destroyed successfully
45+
/// * `Err(DestroySubcommandError)` - Destroy operation failed
46+
///
47+
/// # Errors
48+
///
49+
/// Returns `DestroySubcommandError` when:
50+
/// * Environment name is invalid or contains special characters
51+
/// * Working directory is not accessible or doesn't exist
52+
/// * Environment is not found in the working directory
53+
/// * Infrastructure destruction fails (OpenTofu/LXD errors)
54+
/// * File system operations fail (permission errors, disk space)
55+
///
56+
/// # Examples
57+
///
58+
/// ```rust
59+
/// use std::path::Path;
60+
/// use std::sync::Arc;
61+
/// use torrust_tracker_deployer_lib::presentation::controllers::destroy;
62+
/// use torrust_tracker_deployer_lib::presentation::dispatch::context::ExecutionContext;
63+
/// use torrust_tracker_deployer_lib::bootstrap::container::Container;
64+
/// use torrust_tracker_deployer_lib::presentation::user_output::VerbosityLevel;
65+
///
66+
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
67+
/// let container = Arc::new(Container::new(VerbosityLevel::Normal));
68+
/// let context = ExecutionContext::new(container);
69+
/// let working_dir = Path::new("./test");
70+
///
71+
/// destroy::handle("my-env", working_dir, &context)?;
72+
/// # Ok(())
73+
/// # }
74+
/// ```
75+
#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance
76+
pub fn handle(
77+
environment_name: &str,
78+
working_dir: &std::path::Path,
79+
context: &crate::presentation::dispatch::context::ExecutionContext,
80+
) -> Result<(), DestroySubcommandError> {
81+
handle_destroy_command(
82+
environment_name,
83+
working_dir,
84+
context.repository_factory(),
85+
context.clock(),
86+
&context.user_output(),
87+
)
88+
}
89+
90+
// ============================================================================
91+
// INTERMEDIATE API (DIRECT DEPENDENCY INJECTION)
92+
// ============================================================================
93+
94+
/// Handle the destroy command
95+
///
96+
/// This is a thin wrapper over `DestroyCommandController` that serves as
97+
/// the public entry point for the destroy command.
98+
///
99+
/// # Arguments
100+
///
101+
/// * `environment_name` - The name of the environment to destroy
102+
/// * `working_dir` - Root directory for environment data storage
103+
/// * `repository_factory` - Factory for creating environment repositories
104+
/// * `clock` - Clock service for timing operations
105+
/// * `user_output` - Shared user output service for consistent output formatting
106+
///
107+
/// # Errors
108+
///
109+
/// Returns an error if:
110+
/// - Environment name is invalid (format validation fails)
111+
/// - Environment cannot be loaded from repository
112+
/// - Infrastructure teardown fails
113+
/// - Progress reporting encounters a poisoned mutex
114+
///
115+
/// All errors include detailed context and actionable troubleshooting guidance.
116+
///
117+
/// # Returns
118+
///
119+
/// Returns `Ok(())` on success, or a `DestroySubcommandError` on failure.
120+
///
121+
/// # Example
122+
///
123+
/// Using with Container and `ExecutionContext` (recommended):
124+
///
125+
/// ```rust
126+
/// use std::path::Path;
127+
/// use std::sync::Arc;
128+
/// use torrust_tracker_deployer_lib::bootstrap::Container;
129+
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
130+
/// use torrust_tracker_deployer_lib::presentation::controllers::destroy;
131+
/// use torrust_tracker_deployer_lib::presentation::user_output::VerbosityLevel;
132+
///
133+
/// let container = Container::new(VerbosityLevel::Normal);
134+
/// let context = ExecutionContext::new(Arc::new(container));
135+
///
136+
/// if let Err(e) = destroy::handle("test-env", Path::new("."), &context) {
137+
/// eprintln!("Destroy failed: {e}");
138+
/// eprintln!("Help: {}", e.help());
139+
/// }
140+
/// ```
141+
///
142+
/// Direct usage (for testing or specialized scenarios):
143+
///
144+
/// ```rust
145+
/// use std::path::Path;
146+
/// use std::sync::{Arc, Mutex};
147+
/// use torrust_tracker_deployer_lib::presentation::controllers::destroy;
148+
/// use torrust_tracker_deployer_lib::presentation::user_output::{UserOutput, VerbosityLevel};
149+
/// use torrust_tracker_deployer_lib::infrastructure::persistence::repository_factory::RepositoryFactory;
150+
/// use torrust_tracker_deployer_lib::presentation::commands::constants::DEFAULT_LOCK_TIMEOUT;
151+
/// use torrust_tracker_deployer_lib::shared::SystemClock;
152+
///
153+
/// let user_output = Arc::new(Mutex::new(UserOutput::new(VerbosityLevel::Normal)));
154+
/// let repository_factory = Arc::new(RepositoryFactory::new(DEFAULT_LOCK_TIMEOUT));
155+
/// let clock = Arc::new(SystemClock);
156+
/// if let Err(e) = destroy::handle_destroy_command("test-env", Path::new("."), repository_factory, clock, &user_output) {
157+
/// eprintln!("Destroy failed: {e}");
158+
/// eprintln!("Help: {}", e.help());
159+
/// }
160+
/// ```
161+
#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance
162+
#[allow(clippy::needless_pass_by_value)] // Arc parameters are moved to constructor for ownership
163+
pub fn handle_destroy_command(
164+
environment_name: &str,
165+
working_dir: &std::path::Path,
166+
repository_factory: Arc<RepositoryFactory>,
167+
clock: Arc<dyn Clock>,
168+
user_output: &Arc<Mutex<UserOutput>>,
169+
) -> Result<(), DestroySubcommandError> {
170+
DestroyCommandController::new(
171+
working_dir.to_path_buf(),
172+
repository_factory,
173+
clock,
174+
user_output.clone(),
175+
)
176+
.execute(environment_name)
177+
}
178+
179+
// ============================================================================
180+
// PRESENTATION LAYER CONTROLLER (IMPLEMENTATION DETAILS)
29181
// ============================================================================
30182

31183
/// Presentation layer controller for destroy command workflow
@@ -179,162 +331,6 @@ impl DestroyCommandController {
179331
}
180332
}
181333

182-
// ============================================================================
183-
// PUBLIC ENTRY POINT
184-
// ============================================================================
185-
186-
/// Handle the destroy command
187-
///
188-
/// This is a thin wrapper over `DestroyCommandController` that serves as
189-
/// the public entry point for the destroy command.
190-
///
191-
/// # Arguments
192-
///
193-
/// * `environment_name` - The name of the environment to destroy
194-
/// * `working_dir` - Root directory for environment data storage
195-
/// * `repository_factory` - Factory for creating environment repositories
196-
/// * `clock` - Clock service for timing operations
197-
/// * `user_output` - Shared user output service for consistent output formatting
198-
///
199-
/// # Errors
200-
///
201-
/// Returns an error if:
202-
/// - Environment name is invalid (format validation fails)
203-
/// - Environment cannot be loaded from repository
204-
/// - Infrastructure teardown fails
205-
/// - Progress reporting encounters a poisoned mutex
206-
///
207-
/// All errors include detailed context and actionable troubleshooting guidance.
208-
///
209-
/// # Returns
210-
///
211-
/// Returns `Ok(())` on success, or a `DestroySubcommandError` on failure.
212-
///
213-
/// # Example
214-
///
215-
/// Using with Container and `ExecutionContext` (recommended):
216-
///
217-
/// ```rust
218-
/// use std::path::Path;
219-
/// use std::sync::Arc;
220-
/// use torrust_tracker_deployer_lib::bootstrap::Container;
221-
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
222-
/// use torrust_tracker_deployer_lib::presentation::controllers::destroy;
223-
/// use torrust_tracker_deployer_lib::presentation::user_output::VerbosityLevel;
224-
///
225-
/// let container = Container::new(VerbosityLevel::Normal);
226-
/// let context = ExecutionContext::new(Arc::new(container));
227-
///
228-
/// if let Err(e) = destroy::handle("test-env", Path::new("."), &context) {
229-
/// eprintln!("Destroy failed: {e}");
230-
/// eprintln!("Help: {}", e.help());
231-
/// }
232-
/// ```
233-
///
234-
/// Direct usage (for testing or specialized scenarios):
235-
///
236-
/// ```rust
237-
/// use std::path::Path;
238-
/// use std::sync::{Arc, Mutex};
239-
/// use torrust_tracker_deployer_lib::presentation::controllers::destroy;
240-
/// use torrust_tracker_deployer_lib::presentation::user_output::{UserOutput, VerbosityLevel};
241-
/// use torrust_tracker_deployer_lib::infrastructure::persistence::repository_factory::RepositoryFactory;
242-
/// use torrust_tracker_deployer_lib::presentation::commands::constants::DEFAULT_LOCK_TIMEOUT;
243-
/// use torrust_tracker_deployer_lib::shared::SystemClock;
244-
///
245-
/// let user_output = Arc::new(Mutex::new(UserOutput::new(VerbosityLevel::Normal)));
246-
/// let repository_factory = Arc::new(RepositoryFactory::new(DEFAULT_LOCK_TIMEOUT));
247-
/// let clock = Arc::new(SystemClock);
248-
/// if let Err(e) = destroy::handle_destroy_command("test-env", Path::new("."), repository_factory, clock, &user_output) {
249-
/// eprintln!("Destroy failed: {e}");
250-
/// eprintln!("Help: {}", e.help());
251-
/// }
252-
/// ```
253-
#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance
254-
#[allow(clippy::needless_pass_by_value)] // Arc parameters are moved to constructor for ownership
255-
pub fn handle_destroy_command(
256-
environment_name: &str,
257-
working_dir: &std::path::Path,
258-
repository_factory: Arc<RepositoryFactory>,
259-
clock: Arc<dyn Clock>,
260-
user_output: &Arc<Mutex<UserOutput>>,
261-
) -> Result<(), DestroySubcommandError> {
262-
DestroyCommandController::new(
263-
working_dir.to_path_buf(),
264-
repository_factory,
265-
clock,
266-
user_output.clone(),
267-
)
268-
.execute(environment_name)
269-
}
270-
271-
// ============================================================================
272-
// EXECUTION CONTEXT API
273-
// ============================================================================
274-
275-
/// Handle destroy command using `ExecutionContext` pattern
276-
///
277-
/// This function provides a clean interface for destroying deployment environments,
278-
/// integrating with the `ExecutionContext` pattern for dependency injection.
279-
///
280-
/// # Arguments
281-
///
282-
/// * `environment_name` - Name of the environment to destroy
283-
/// * `working_dir` - Working directory path for operations
284-
/// * `context` - Execution context providing access to services
285-
///
286-
/// # Returns
287-
///
288-
/// * `Ok(())` - Environment destroyed successfully
289-
/// * `Err(DestroySubcommandError)` - Destroy operation failed
290-
///
291-
/// # Errors
292-
///
293-
/// Returns `DestroySubcommandError` when:
294-
/// * Environment name is invalid or contains special characters
295-
/// * Working directory is not accessible or doesn't exist
296-
/// * Environment is not found in the working directory
297-
/// * Infrastructure destruction fails (OpenTofu/LXD errors)
298-
/// * File system operations fail (permission errors, disk space)
299-
///
300-
/// # Examples
301-
///
302-
/// ```rust
303-
/// use std::path::Path;
304-
/// use std::sync::Arc;
305-
/// use torrust_tracker_deployer_lib::presentation::controllers::destroy;
306-
/// use torrust_tracker_deployer_lib::presentation::dispatch::context::ExecutionContext;
307-
/// use torrust_tracker_deployer_lib::bootstrap::container::Container;
308-
/// use torrust_tracker_deployer_lib::presentation::user_output::VerbosityLevel;
309-
///
310-
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
311-
/// let container = Arc::new(Container::new(VerbosityLevel::Normal));
312-
/// let context = ExecutionContext::new(container);
313-
/// let working_dir = Path::new("./test");
314-
///
315-
/// destroy::handle("my-env", working_dir, &context)?;
316-
/// # Ok(())
317-
/// # }
318-
/// ```
319-
#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance
320-
pub fn handle(
321-
environment_name: &str,
322-
working_dir: &std::path::Path,
323-
context: &crate::presentation::dispatch::context::ExecutionContext,
324-
) -> Result<(), DestroySubcommandError> {
325-
handle_destroy_command(
326-
environment_name,
327-
working_dir,
328-
context.repository_factory(),
329-
context.clock(),
330-
&context.user_output(),
331-
)
332-
}
333-
334-
// ============================================================================
335-
// TESTS
336-
// ============================================================================
337-
338334
#[cfg(test)]
339335
mod tests {
340336
use super::*;

0 commit comments

Comments
 (0)