33//! This module handles the destroy command execution at the presentation layer,
44//! including environment validation, repository initialization, and user interaction.
55
6+ use std:: path:: PathBuf ;
67use std:: sync:: { Arc , Mutex } ;
78
9+ use crate :: application:: command_handlers:: DestroyCommandHandler ;
810use crate :: domain:: environment:: name:: EnvironmentName ;
11+ use crate :: domain:: environment:: state:: Destroyed ;
12+ use crate :: domain:: Environment ;
13+ use crate :: presentation:: commands:: context:: CommandContext ;
914use crate :: presentation:: commands:: factory:: CommandHandlerFactory ;
1015use crate :: presentation:: progress:: ProgressReporter ;
1116use crate :: presentation:: user_output:: UserOutput ;
1217
1318use super :: errors:: DestroySubcommandError ;
1419
15- /// Handle the destroy command
20+ // ============================================================================
21+ // CONSTANTS
22+ // ============================================================================
23+
24+ /// Number of main steps in the destroy workflow
25+ const DESTROY_WORKFLOW_STEPS : usize = 3 ;
26+
27+ // ============================================================================
28+ // PRESENTATION LAYER CONTROLLER
29+ // ============================================================================
30+
31+ /// Presentation layer controller for destroy command workflow
32+ ///
33+ /// Coordinates user interaction, progress reporting, and input validation
34+ /// before delegating to the application layer `DestroyCommandHandler`.
35+ ///
36+ /// # Responsibilities
1637///
17- /// This function orchestrates the environment destruction workflow with progress reporting:
18- /// 1. Validating the environment name
19- /// 2. Tearing down infrastructure
20- /// 3. Cleaning up resources
38+ /// - Validate user input (environment name format)
39+ /// - Show progress updates to the user
40+ /// - Format success/error messages for display
41+ /// - Delegate business logic to application layer
42+ ///
43+ /// # Architecture
44+ ///
45+ /// This controller sits in the presentation layer and handles all user-facing
46+ /// concerns. It delegates actual business logic to the application layer's
47+ /// `DestroyCommandHandler`, maintaining clear separation of concerns.
48+ pub struct DestroyCommandController {
49+ factory : CommandHandlerFactory ,
50+ ctx : CommandContext ,
51+ progress : ProgressReporter ,
52+ }
53+
54+ impl DestroyCommandController {
55+ /// Create a new destroy command controller
56+ ///
57+ /// # Arguments
58+ ///
59+ /// * `working_dir` - Root directory for environment data storage
60+ /// * `user_output` - Shared user output service for consistent formatting
61+ pub fn new ( working_dir : PathBuf , user_output : Arc < Mutex < UserOutput > > ) -> Self {
62+ let factory = CommandHandlerFactory :: new ( ) ;
63+ let ctx = factory. create_context ( working_dir, user_output. clone ( ) ) ;
64+ let progress = ProgressReporter :: new ( user_output, DESTROY_WORKFLOW_STEPS ) ;
65+
66+ Self {
67+ factory,
68+ ctx,
69+ progress,
70+ }
71+ }
72+
73+ /// Execute the complete destroy workflow
74+ ///
75+ /// Orchestrates all steps of the destroy command:
76+ /// 1. Validate environment name
77+ /// 2. Initialize dependencies
78+ /// 3. Tear down infrastructure
79+ /// 4. Complete with success message
80+ ///
81+ /// # Arguments
82+ ///
83+ /// * `environment_name` - The name of the environment to destroy
84+ ///
85+ /// # Errors
86+ ///
87+ /// Returns an error if:
88+ /// - Environment name is invalid (format validation fails)
89+ /// - Environment cannot be loaded from repository
90+ /// - Infrastructure teardown fails
91+ /// - Progress reporting encounters a poisoned mutex
92+ ///
93+ /// # Returns
94+ ///
95+ /// Returns `Ok(())` on success, or a `DestroySubcommandError` if any step fails.
96+ #[ allow( clippy:: result_large_err) ]
97+ pub fn execute ( & mut self , environment_name : & str ) -> Result < ( ) , DestroySubcommandError > {
98+ let env_name = self . validate_environment_name ( environment_name) ?;
99+ let handler = self . initialize_dependencies ( ) ?;
100+ let _destroyed = self . tear_down_infrastructure ( & handler, & env_name) ?;
101+ self . complete_workflow ( environment_name) ?;
102+ Ok ( ( ) )
103+ }
104+
105+ /// Validate the environment name format
106+ ///
107+ /// Shows progress to user and validates that the environment name
108+ /// meets domain requirements (1-63 chars, alphanumeric + hyphens).
109+ #[ allow( clippy:: result_large_err) ]
110+ fn validate_environment_name (
111+ & mut self ,
112+ name : & str ,
113+ ) -> Result < EnvironmentName , DestroySubcommandError > {
114+ self . progress . start_step ( "Validating environment" ) ?;
115+
116+ let env_name = EnvironmentName :: new ( name. to_string ( ) ) . map_err ( |source| {
117+ DestroySubcommandError :: InvalidEnvironmentName {
118+ name : name. to_string ( ) ,
119+ source,
120+ }
121+ } ) ?;
122+
123+ self . progress
124+ . complete_step ( Some ( & format ! ( "Environment name validated: {name}" ) ) ) ?;
125+
126+ Ok ( env_name)
127+ }
128+
129+ /// Initialize application layer dependencies
130+ ///
131+ /// Creates the application layer command handler with all required
132+ /// dependencies (repository, clock, etc.).
133+ #[ allow( clippy:: result_large_err) ]
134+ fn initialize_dependencies ( & mut self ) -> Result < DestroyCommandHandler , DestroySubcommandError > {
135+ self . progress . start_step ( "Initializing dependencies" ) ?;
136+ let handler = self . factory . create_destroy_handler ( & self . ctx ) ;
137+ self . progress . complete_step ( None ) ?;
138+ Ok ( handler)
139+ }
140+
141+ /// Execute infrastructure teardown via application layer
142+ ///
143+ /// Delegates to the application layer `DestroyCommandHandler` to
144+ /// orchestrate the actual infrastructure destruction workflow.
145+ #[ allow( clippy:: result_large_err) ]
146+ fn tear_down_infrastructure (
147+ & mut self ,
148+ handler : & DestroyCommandHandler ,
149+ env_name : & EnvironmentName ,
150+ ) -> Result < Environment < Destroyed > , DestroySubcommandError > {
151+ self . progress . start_step ( "Tearing down infrastructure" ) ?;
152+
153+ let destroyed = handler. execute ( env_name) . map_err ( |source| {
154+ DestroySubcommandError :: DestroyOperationFailed {
155+ name : env_name. to_string ( ) ,
156+ source,
157+ }
158+ } ) ?;
159+
160+ self . progress
161+ . complete_step ( Some ( "Infrastructure torn down" ) ) ?;
162+ Ok ( destroyed)
163+ }
164+
165+ /// Complete the workflow with success message
166+ ///
167+ /// Shows final success message to the user with workflow summary.
168+ #[ allow( clippy:: result_large_err) ]
169+ fn complete_workflow ( & mut self , name : & str ) -> Result < ( ) , DestroySubcommandError > {
170+ self . progress
171+ . complete ( & format ! ( "Environment '{name}' destroyed successfully" ) ) ?;
172+ Ok ( ( ) )
173+ }
174+ }
175+
176+ // ============================================================================
177+ // PUBLIC ENTRY POINT
178+ // ============================================================================
179+
180+ /// Handle the destroy command
21181///
22- /// Each step is tracked and timed using `ProgressReporter` for clear user feedback.
182+ /// This is a thin wrapper over `DestroyCommandController` that serves as
183+ /// the public entry point for the destroy command.
23184///
24185/// # Arguments
25186///
26187/// * `environment_name` - The name of the environment to destroy
27188/// * `working_dir` - Root directory for environment data storage
28189/// * `user_output` - Shared user output service for consistent output formatting
29190///
30- /// # Returns
31- ///
32- /// Returns `Ok(())` on success, or a `DestroySubcommandError` if:
33- /// - Environment name is invalid
34- /// - Environment cannot be loaded
35- /// - Destruction fails
36- ///
37191/// # Errors
38192///
39- /// This function will return an error if the environment name is invalid,
40- /// the environment cannot be loaded, or the destruction process fails.
193+ /// Returns an error if:
194+ /// - Environment name is invalid (format validation fails)
195+ /// - Environment cannot be loaded from repository
196+ /// - Infrastructure teardown fails
197+ /// - Progress reporting encounters a poisoned mutex
198+ ///
41199/// All errors include detailed context and actionable troubleshooting guidance.
42200///
201+ /// # Returns
202+ ///
203+ /// Returns `Ok(())` on success, or a `DestroySubcommandError` on failure.
204+ ///
43205/// # Example
44206///
45207/// ```rust
@@ -60,48 +222,14 @@ pub fn handle_destroy_command(
60222 working_dir : & std:: path:: Path ,
61223 user_output : & Arc < Mutex < UserOutput > > ,
62224) -> Result < ( ) , DestroySubcommandError > {
63- // Create factory and context with all shared dependencies
64- let factory = CommandHandlerFactory :: new ( ) ;
65- let ctx = factory. create_context ( working_dir. to_path_buf ( ) , user_output. clone ( ) ) ;
66-
67- // Create progress reporter for 3 main steps
68- let mut progress = ProgressReporter :: new ( ctx. user_output ( ) . clone ( ) , 3 ) ;
69-
70- // Step 1: Validate environment name
71- progress. start_step ( "Validating environment" ) ?;
72- let env_name = EnvironmentName :: new ( environment_name. to_string ( ) ) . map_err ( |source| {
73- DestroySubcommandError :: InvalidEnvironmentName {
74- name : environment_name. to_string ( ) ,
75- source,
76- }
77- } ) ?;
78- progress. complete_step ( Some ( & format ! (
79- "Environment name validated: {environment_name}"
80- ) ) ) ?;
81-
82- // Step 2: Initialize dependencies
83- progress. start_step ( "Initializing dependencies" ) ?;
84- let command_handler = factory. create_destroy_handler ( & ctx) ;
85- progress. complete_step ( None ) ?;
86-
87- // Step 3: Execute destroy command (tear down infrastructure)
88- progress. start_step ( "Tearing down infrastructure" ) ?;
89- let _destroyed_env = command_handler. execute ( & env_name) . map_err ( |source| {
90- DestroySubcommandError :: DestroyOperationFailed {
91- name : environment_name. to_string ( ) ,
92- source,
93- }
94- } ) ?;
95- progress. complete_step ( Some ( "Infrastructure torn down" ) ) ?;
96-
97- // Complete with summary
98- progress. complete ( & format ! (
99- "Environment '{environment_name}' destroyed successfully"
100- ) ) ?;
101-
102- Ok ( ( ) )
225+ DestroyCommandController :: new ( working_dir. to_path_buf ( ) , user_output. clone ( ) )
226+ . execute ( environment_name)
103227}
104228
229+ // ============================================================================
230+ // TESTS
231+ // ============================================================================
232+
105233#[ cfg( test) ]
106234mod tests {
107235 use super :: * ;
0 commit comments