From a5f337e86bae819f1126f77f0fc84ca4fe8dca86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:31:22 +0000 Subject: [PATCH 1/5] Initial plan From 6cc2ca00061ab8c23ba06d44be8878f408333838 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:37:11 +0000 Subject: [PATCH 2/5] refactor: rename destroy/command.rs to handler.rs Co-authored-by: josecelano <58816+josecelano@users.noreply.github.com> --- .../commands/destroy/{command.rs => handler.rs} | 0 src/presentation/commands/destroy/mod.rs | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/presentation/commands/destroy/{command.rs => handler.rs} (100%) diff --git a/src/presentation/commands/destroy/command.rs b/src/presentation/commands/destroy/handler.rs similarity index 100% rename from src/presentation/commands/destroy/command.rs rename to src/presentation/commands/destroy/handler.rs diff --git a/src/presentation/commands/destroy/mod.rs b/src/presentation/commands/destroy/mod.rs index ebdf592c..8ed516ed 100644 --- a/src/presentation/commands/destroy/mod.rs +++ b/src/presentation/commands/destroy/mod.rs @@ -12,7 +12,7 @@ //! ## Components //! //! - `errors` - Presentation layer error types with `.help()` methods -//! - `command` - Main command handler orchestrating the workflow +//! - `handler` - Main command handler orchestrating the workflow //! //! ## Usage Example //! @@ -26,12 +26,12 @@ //! } //! ``` -pub mod command; pub mod errors; +pub mod handler; #[cfg(test)] mod tests; // Re-export commonly used types for convenience -pub use command::handle_destroy_command; pub use errors::DestroySubcommandError; +pub use handler::handle_destroy_command; From 863f4659258952d65c4f663c7157c0ae0e4a1f72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:40:22 +0000 Subject: [PATCH 3/5] refactor: organize create command into handler and subcommands Co-authored-by: josecelano <58816+josecelano@users.noreply.github.com> --- src/presentation/commands/create/handler.rs | 43 ++++++++ src/presentation/commands/create/mod.rs | 8 +- .../environment.rs} | 102 ++---------------- .../commands/create/subcommands/mod.rs | 10 ++ .../commands/create/subcommands/template.rs | 66 ++++++++++++ 5 files changed, 131 insertions(+), 98 deletions(-) create mode 100644 src/presentation/commands/create/handler.rs rename src/presentation/commands/create/{subcommand.rs => subcommands/environment.rs} (73%) create mode 100644 src/presentation/commands/create/subcommands/mod.rs create mode 100644 src/presentation/commands/create/subcommands/template.rs diff --git a/src/presentation/commands/create/handler.rs b/src/presentation/commands/create/handler.rs new file mode 100644 index 00000000..ce81ba8c --- /dev/null +++ b/src/presentation/commands/create/handler.rs @@ -0,0 +1,43 @@ +//! Create Command Handler +//! +//! This module handles the create command execution at the presentation layer, +//! routing between different subcommands (environment creation or template generation). + +use std::path::Path; + +use crate::presentation::cli::commands::CreateAction; + +use super::errors::CreateSubcommandError; +use super::subcommands; + +/// Handle the create command with its subcommands +/// +/// This function routes between different create subcommands (environment or template). +/// +/// # Arguments +/// +/// * `action` - The create action to perform (environment creation or template generation) +/// * `working_dir` - Root directory for environment data storage +/// +/// # Returns +/// +/// Returns `Ok(())` on success, or a `CreateSubcommandError` on failure. +/// +/// # Errors +/// +/// Returns an error if the subcommand execution fails. +#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance +pub fn handle_create_command( + action: CreateAction, + working_dir: &Path, +) -> Result<(), CreateSubcommandError> { + match action { + CreateAction::Environment { env_file } => { + subcommands::handle_environment_creation(&env_file, working_dir) + } + CreateAction::Template { output_path } => { + let template_path = output_path.unwrap_or_else(CreateAction::default_template_path); + subcommands::handle_template_generation(&template_path) + } + } +} diff --git a/src/presentation/commands/create/mod.rs b/src/presentation/commands/create/mod.rs index f4c5ef1a..4dd0ee92 100644 --- a/src/presentation/commands/create/mod.rs +++ b/src/presentation/commands/create/mod.rs @@ -15,7 +15,8 @@ //! //! - `config_loader` - Figment integration for JSON configuration loading //! - `errors` - Presentation layer error types with `.help()` methods -//! - `subcommand` - Main command handler orchestrating the workflow +//! - `handler` - Main command handler routing between subcommands +//! - `subcommands` - Individual subcommand implementations (environment, template) //! //! ## Usage Example //! @@ -36,7 +37,8 @@ pub mod config_loader; pub mod errors; -pub mod subcommand; +pub mod handler; +pub mod subcommands; #[cfg(test)] mod tests; @@ -44,4 +46,4 @@ mod tests; // Re-export commonly used types for convenience pub use config_loader::ConfigLoader; pub use errors::{ConfigFormat, CreateSubcommandError}; -pub use subcommand::handle_create_command; +pub use handler::handle_create_command; diff --git a/src/presentation/commands/create/subcommand.rs b/src/presentation/commands/create/subcommands/environment.rs similarity index 73% rename from src/presentation/commands/create/subcommand.rs rename to src/presentation/commands/create/subcommands/environment.rs index 6c86168c..94472a85 100644 --- a/src/presentation/commands/create/subcommand.rs +++ b/src/presentation/commands/create/subcommands/environment.rs @@ -1,8 +1,7 @@ -//! Create Subcommand Handler +//! Environment Creation Subcommand //! -//! This module handles the create subcommand execution at the presentation layer, -//! including configuration file loading, argument processing, user interaction, -//! and command execution. +//! This module handles the environment creation subcommand for creating +//! deployment environments from configuration files. use std::path::Path; use std::sync::Arc; @@ -11,99 +10,11 @@ use std::time::Duration; use crate::application::command_handlers::create::CreateCommandHandler; use crate::domain::config::EnvironmentCreationConfig; use crate::infrastructure::persistence::repository_factory::RepositoryFactory; -use crate::presentation::cli::commands::CreateAction; use crate::presentation::user_output::{UserOutput, VerbosityLevel}; use crate::shared::{Clock, SystemClock}; -use super::config_loader::ConfigLoader; -use super::errors::CreateSubcommandError; - -/// Handle the create command with its subcommands -/// -/// This function routes between different create subcommands (environment or template). -/// -/// # Arguments -/// -/// * `action` - The create action to perform (environment creation or template generation) -/// * `working_dir` - Root directory for environment data storage -/// -/// # Returns -/// -/// Returns `Ok(())` on success, or a `CreateSubcommandError` on failure. -/// -/// # Errors -/// -/// Returns an error if the subcommand execution fails. -#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance -pub fn handle_create_command( - action: CreateAction, - working_dir: &Path, -) -> Result<(), CreateSubcommandError> { - match action { - CreateAction::Environment { env_file } => { - handle_environment_creation(&env_file, working_dir) - } - CreateAction::Template { output_path } => { - let template_path = output_path.unwrap_or_else(CreateAction::default_template_path); - handle_template_generation(&template_path) - } - } -} - -/// Handle template generation -/// -/// This function generates a configuration template file with placeholder values -/// that users can edit to create their own environment configurations. -/// -/// # Arguments -/// -/// * `output_path` - Path where the template file should be created -/// -/// # Returns -/// -/// Returns `Ok(())` on success, or a `CreateSubcommandError` on failure. -/// -/// # Errors -/// -/// Returns an error if template file creation fails. -#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance -fn handle_template_generation(output_path: &Path) -> Result<(), CreateSubcommandError> { - // Create user output for progress messages - let mut output = UserOutput::new(VerbosityLevel::Normal); - - output.progress("Generating configuration template..."); - - // Call existing domain method - template generation implemented in PR #48 - // This is async, so we need to use tokio runtime - tokio::runtime::Runtime::new() - .expect("Failed to create tokio runtime") - .block_on(async { - EnvironmentCreationConfig::generate_template_file(output_path) - .await - .map_err(CreateSubcommandError::TemplateGenerationFailed) - })?; - - output.success(&format!( - "Configuration template generated: {}", - output_path.display() - )); - println!(); - println!("Next steps:"); - println!("1. Edit the template file and replace placeholder values:"); - println!(" - REPLACE_WITH_ENVIRONMENT_NAME: Choose a unique environment name (e.g., 'dev', 'staging')"); - println!(" - REPLACE_WITH_SSH_PRIVATE_KEY_PATH: Path to your SSH private key"); - println!(" - REPLACE_WITH_SSH_PUBLIC_KEY_PATH: Path to your SSH public key"); - println!("2. Review default values:"); - println!(" - username: 'torrust' (can be changed if needed)"); - println!(" - port: 22 (standard SSH port)"); - println!("3. Create the environment:"); - println!( - " torrust-tracker-deployer create environment --env-file {}", - output_path.display() - ); - - Ok(()) -} +use super::super::config_loader::ConfigLoader; +use super::super::errors::CreateSubcommandError; /// Handle environment creation from configuration file /// @@ -135,7 +46,7 @@ fn handle_template_generation(output_path: &Path) -> Result<(), CreateSubcommand /// loaded, parsed, validated, or if the create command execution fails. /// All errors include detailed context and actionable troubleshooting guidance. #[allow(clippy::result_large_err)] // Error contains detailed context for user guidance -fn handle_environment_creation( +pub fn handle_environment_creation( env_file: &Path, working_dir: &Path, ) -> Result<(), CreateSubcommandError> { @@ -369,3 +280,4 @@ mod tests { ); } } + diff --git a/src/presentation/commands/create/subcommands/mod.rs b/src/presentation/commands/create/subcommands/mod.rs new file mode 100644 index 00000000..507af20b --- /dev/null +++ b/src/presentation/commands/create/subcommands/mod.rs @@ -0,0 +1,10 @@ +//! Create Subcommands Module +//! +//! This module contains the individual subcommands for the create command. + +pub mod environment; +pub mod template; + +// Re-export subcommand handlers for convenience +pub use environment::handle_environment_creation; +pub use template::handle_template_generation; diff --git a/src/presentation/commands/create/subcommands/template.rs b/src/presentation/commands/create/subcommands/template.rs new file mode 100644 index 00000000..f27c743e --- /dev/null +++ b/src/presentation/commands/create/subcommands/template.rs @@ -0,0 +1,66 @@ +//! Template Generation Subcommand +//! +//! This module handles the template generation subcommand for creating +//! configuration file templates with placeholder values. + +use std::path::Path; + +use crate::domain::config::EnvironmentCreationConfig; +use crate::presentation::user_output::{UserOutput, VerbosityLevel}; + +use super::super::errors::CreateSubcommandError; + +/// Handle template generation +/// +/// This function generates a configuration template file with placeholder values +/// that users can edit to create their own environment configurations. +/// +/// # Arguments +/// +/// * `output_path` - Path where the template file should be created +/// +/// # Returns +/// +/// Returns `Ok(())` on success, or a `CreateSubcommandError` on failure. +/// +/// # Errors +/// +/// Returns an error if template file creation fails. +#[allow(clippy::result_large_err)] // Error contains detailed context for user guidance +pub fn handle_template_generation(output_path: &Path) -> Result<(), CreateSubcommandError> { + // Create user output for progress messages + let mut output = UserOutput::new(VerbosityLevel::Normal); + + output.progress("Generating configuration template..."); + + // Call existing domain method - template generation implemented in PR #48 + // This is async, so we need to use tokio runtime + tokio::runtime::Runtime::new() + .expect("Failed to create tokio runtime") + .block_on(async { + EnvironmentCreationConfig::generate_template_file(output_path) + .await + .map_err(CreateSubcommandError::TemplateGenerationFailed) + })?; + + output.success(&format!( + "Configuration template generated: {}", + output_path.display() + )); + println!(); + println!("Next steps:"); + println!("1. Edit the template file and replace placeholder values:"); + println!(" - REPLACE_WITH_ENVIRONMENT_NAME: Choose a unique environment name (e.g., 'dev', 'staging')"); + println!(" - REPLACE_WITH_SSH_PRIVATE_KEY_PATH: Path to your SSH private key"); + println!(" - REPLACE_WITH_SSH_PUBLIC_KEY_PATH: Path to your SSH public key"); + println!("2. Review default values:"); + println!(" - username: 'torrust' (can be changed if needed)"); + println!(" - port: 22 (standard SSH port)"); + println!("3. Create the environment:"); + println!( + " torrust-tracker-deployer create environment --env-file {}", + output_path.display() + ); + + Ok(()) +} From 06f10aff0eade969bd31b60f419ead8a23541d99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:41:47 +0000 Subject: [PATCH 4/5] docs: add command module structure patterns Co-authored-by: josecelano <58816+josecelano@users.noreply.github.com> --- docs/contributing/module-organization.md | 143 +++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/docs/contributing/module-organization.md b/docs/contributing/module-organization.md index 4b686426..262d5487 100644 --- a/docs/contributing/module-organization.md +++ b/docs/contributing/module-organization.md @@ -449,6 +449,149 @@ These guidelines are general principles, not absolute rules. Consider deviating Use your judgment, but **always prioritize readability and maintainability**. +## 📂 Command Module Structure Patterns + +For presentation layer commands in `src/presentation/commands/`, we follow standardized folder structures that make it clear whether a command has subcommands or is a simple single-purpose command. + +### Pattern 1: Simple Commands (No Subcommands) + +For commands that perform a single operation (like `destroy`): + +``` +src/presentation/commands/destroy/ + ├── mod.rs // Module documentation and re-exports + ├── handler.rs // Main command implementation + ├── errors.rs // Error types + └── tests/ // Test modules + ├── mod.rs + └── integration.rs +``` + +**Key characteristics:** +- Uses `handler.rs` for the main command logic +- Direct implementation without routing +- Clean and focused on single responsibility + +**Example:** +```rust +// In handler.rs +pub fn handle_destroy_command( + environment_name: &str, + working_dir: &Path, +) -> Result<(), DestroySubcommandError> { + // Direct implementation +} +``` + +### Pattern 2: Commands with Subcommands + +For commands that route to multiple subcommands (like `create`): + +``` +src/presentation/commands/create/ + ├── mod.rs // Module documentation and re-exports + ├── handler.rs // Router that delegates to subcommands + ├── errors.rs // Shared error types + ├── config_loader.rs // Shared utilities (if needed) + ├── subcommands/ // 🆕 Dedicated subcommands folder + │ ├── mod.rs // Subcommands module and re-exports + │ ├── environment.rs // Environment creation subcommand + │ └── template.rs // Template generation subcommand + └── tests/ // Test modules + ├── mod.rs + ├── integration.rs + └── fixtures.rs +``` + +**Key characteristics:** +- `handler.rs` acts as a simple router/dispatcher +- Each subcommand has its own focused module in `subcommands/` +- Subcommands are isolated and single-responsibility +- Easy to add new subcommands without cluttering main files + +**Example:** +```rust +// In handler.rs (router) +pub fn handle_create_command( + action: CreateAction, + working_dir: &Path, +) -> Result<(), CreateSubcommandError> { + match action { + CreateAction::Environment { env_file } => { + subcommands::handle_environment_creation(&env_file, working_dir) + } + CreateAction::Template { output_path } => { + let template_path = output_path.unwrap_or_else(CreateAction::default_template_path); + subcommands::handle_template_generation(&template_path) + } + } +} + +// In subcommands/environment.rs +pub fn handle_environment_creation( + env_file: &Path, + working_dir: &Path, +) -> Result<(), CreateSubcommandError> { + // Focused implementation for environment creation +} + +// In subcommands/template.rs +pub fn handle_template_generation( + output_path: &Path, +) -> Result<(), CreateSubcommandError> { + // Focused implementation for template generation +} +``` + +### When to Use Each Pattern + +**Use Pattern 1 (Simple Commands)** when: +- The command performs a single, focused operation +- No routing or branching logic is needed +- The implementation fits naturally in one module + +**Use Pattern 2 (Commands with Subcommands)** when: +- The command has multiple distinct subcommands +- Each subcommand has significant implementation +- You want to isolate different behaviors for clarity +- You anticipate adding more subcommands in the future + +### Benefits of These Patterns + +✅ **Clear Visual Distinction**: Folder structure immediately shows command complexity +✅ **Consistent Naming**: All commands use `handler.rs` for their main entry point +✅ **Single Responsibility**: Each subcommand module has one clear purpose +✅ **Easy Extension**: Adding new subcommands is straightforward +✅ **Better Testing**: Each subcommand can be tested independently +✅ **Improved Navigation**: Developers can quickly find the right code + +### Migration Guide + +When refactoring existing commands to follow these patterns: + +1. **For simple commands**: Rename `command.rs` → `handler.rs` +2. **For commands with subcommands**: + - Create `subcommands/` directory + - Move subcommand implementations to individual files in `subcommands/` + - Rename main file to `handler.rs` and simplify to a router + - Update `mod.rs` to include the `subcommands` module + - Update re-exports to use the new structure + +**Example migration:** +```bash +# Before +create/ + └── subcommand.rs (contains all logic) + +# After +create/ + ├── handler.rs (router only) + └── subcommands/ + ├── mod.rs + ├── environment.rs + └── template.rs +``` + ## 🔗 Related Documentation - [Testing Conventions](./testing.md) - How to organize test code From 664e1c7a1ca457bfab7309fe81beff9b1ef27ae7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 21:56:15 +0000 Subject: [PATCH 5/5] fix: address linting issues in code and documentation Co-authored-by: josecelano <58816+josecelano@users.noreply.github.com> --- docs/contributing/module-organization.md | 11 +++++++++-- .../commands/create/subcommands/environment.rs | 1 - .../commands/create/subcommands/template.rs | 5 +++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/contributing/module-organization.md b/docs/contributing/module-organization.md index 262d5487..024dcc65 100644 --- a/docs/contributing/module-organization.md +++ b/docs/contributing/module-organization.md @@ -457,7 +457,7 @@ For presentation layer commands in `src/presentation/commands/`, we follow stand For commands that perform a single operation (like `destroy`): -``` +```text src/presentation/commands/destroy/ ├── mod.rs // Module documentation and re-exports ├── handler.rs // Main command implementation @@ -468,11 +468,13 @@ src/presentation/commands/destroy/ ``` **Key characteristics:** + - Uses `handler.rs` for the main command logic - Direct implementation without routing - Clean and focused on single responsibility **Example:** + ```rust // In handler.rs pub fn handle_destroy_command( @@ -487,7 +489,7 @@ pub fn handle_destroy_command( For commands that route to multiple subcommands (like `create`): -``` +```text src/presentation/commands/create/ ├── mod.rs // Module documentation and re-exports ├── handler.rs // Router that delegates to subcommands @@ -504,12 +506,14 @@ src/presentation/commands/create/ ``` **Key characteristics:** + - `handler.rs` acts as a simple router/dispatcher - Each subcommand has its own focused module in `subcommands/` - Subcommands are isolated and single-responsibility - Easy to add new subcommands without cluttering main files **Example:** + ```rust // In handler.rs (router) pub fn handle_create_command( @@ -546,11 +550,13 @@ pub fn handle_template_generation( ### When to Use Each Pattern **Use Pattern 1 (Simple Commands)** when: + - The command performs a single, focused operation - No routing or branching logic is needed - The implementation fits naturally in one module **Use Pattern 2 (Commands with Subcommands)** when: + - The command has multiple distinct subcommands - Each subcommand has significant implementation - You want to isolate different behaviors for clarity @@ -578,6 +584,7 @@ When refactoring existing commands to follow these patterns: - Update re-exports to use the new structure **Example migration:** + ```bash # Before create/ diff --git a/src/presentation/commands/create/subcommands/environment.rs b/src/presentation/commands/create/subcommands/environment.rs index 94472a85..346c69ee 100644 --- a/src/presentation/commands/create/subcommands/environment.rs +++ b/src/presentation/commands/create/subcommands/environment.rs @@ -280,4 +280,3 @@ mod tests { ); } } - diff --git a/src/presentation/commands/create/subcommands/template.rs b/src/presentation/commands/create/subcommands/template.rs index f27c743e..e61c2b2c 100644 --- a/src/presentation/commands/create/subcommands/template.rs +++ b/src/presentation/commands/create/subcommands/template.rs @@ -26,6 +26,11 @@ use super::super::errors::CreateSubcommandError; /// # Errors /// /// Returns an error if template file creation fails. +/// +/// # Panics +/// +/// Panics if the tokio runtime cannot be created. This is a critical system +/// failure that prevents any async operations from running. #[allow(clippy::result_large_err)] // Error contains detailed context for user guidance pub fn handle_template_generation(output_path: &Path) -> Result<(), CreateSubcommandError> { // Create user output for progress messages