From 8231a4c41ea4ba68622576f0f52081c24fb982f7 Mon Sep 17 00:00:00 2001 From: KrLite Date: Thu, 20 Nov 2025 09:26:51 +0800 Subject: [PATCH 1/9] fix: correct the execution of commands --- src/command/build.rs | 2 +- src/command/builder/prune.rs | 2 +- src/command/images.rs | 2 +- src/command/login.rs | 2 +- src/command/logout.rs | 2 +- src/command/pull.rs | 2 +- src/command/push.rs | 2 +- src/command/search.rs | 2 +- src/command/tag.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/command/build.rs b/src/command/build.rs index 75655d0d..ca8e4e3b 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -1322,7 +1322,7 @@ impl DockerCommand for BuildCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - let output = self.executor.execute_command("docker", args).await?; + let output = self.execute_command(args).await?; // Extract image ID from output let image_id = if self.quiet { diff --git a/src/command/builder/prune.rs b/src/command/builder/prune.rs index ec4e8f43..f253f0f7 100644 --- a/src/command/builder/prune.rs +++ b/src/command/builder/prune.rs @@ -191,7 +191,7 @@ impl DockerCommand for BuilderPruneCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - let output = self.executor.execute_command("docker", args).await?; + let output = self.execute_command(args).await?; let (deleted_cache_ids, space_reclaimed, space_reclaimed_str) = Self::parse_output(&output.stdout); diff --git a/src/command/images.rs b/src/command/images.rs index 0c64e1b5..fc3a7f6c 100644 --- a/src/command/images.rs +++ b/src/command/images.rs @@ -820,7 +820,7 @@ impl DockerCommand for ImagesCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - let output = self.executor.execute_command("docker", args).await?; + let output = self.execute_command(args).await?; let images = self.parse_output(&output); diff --git a/src/command/login.rs b/src/command/login.rs index 1403b577..5550163f 100644 --- a/src/command/login.rs +++ b/src/command/login.rs @@ -234,7 +234,7 @@ impl DockerCommand for LoginCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - let output = self.executor.execute_command("docker", args).await?; + let output = self.execute_command(args).await?; Ok(LoginOutput { output }) } diff --git a/src/command/logout.rs b/src/command/logout.rs index c7f01bbf..b8338d65 100644 --- a/src/command/logout.rs +++ b/src/command/logout.rs @@ -186,7 +186,7 @@ impl DockerCommand for LogoutCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - let output = self.executor.execute_command("docker", args).await?; + let output = self.execute_command(args).await?; Ok(LogoutOutput { output }) } diff --git a/src/command/pull.rs b/src/command/pull.rs index 5540aa88..028789f9 100644 --- a/src/command/pull.rs +++ b/src/command/pull.rs @@ -349,7 +349,7 @@ impl DockerCommand for PullCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - self.executor.execute_command("docker", args).await + self.execute_command(args).await } } diff --git a/src/command/push.rs b/src/command/push.rs index 332009b1..6de8e2da 100644 --- a/src/command/push.rs +++ b/src/command/push.rs @@ -357,7 +357,7 @@ impl DockerCommand for PushCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - self.executor.execute_command("docker", args).await + self.execute_command(args).await } } diff --git a/src/command/search.rs b/src/command/search.rs index 2b38fc86..c905ec51 100644 --- a/src/command/search.rs +++ b/src/command/search.rs @@ -488,7 +488,7 @@ impl DockerCommand for SearchCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - let output = self.executor.execute_command("docker", args).await?; + let output = self.execute_command(args).await?; let repositories = self.parse_output(&output)?; diff --git a/src/command/tag.rs b/src/command/tag.rs index f290438f..4daa4ed6 100644 --- a/src/command/tag.rs +++ b/src/command/tag.rs @@ -130,7 +130,7 @@ impl DockerCommand for TagCommand { async fn execute(&self) -> Result { let args = self.build_command_args(); - self.executor.execute_command("docker", args).await + self.execute_command(args).await } } From aa9265476fa8b89ed1757e1f551f4019fbc09cab Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 12:13:54 +0800 Subject: [PATCH 2/9] feat: remove `get_` prefixes, standardize documents --- src/command.rs | 279 ++++++++++++++--------------- src/command/attach.rs | 4 +- src/command/bake.rs | 8 +- src/command/build.rs | 4 +- src/command/builder/build.rs | 4 +- src/command/builder/prune.rs | 6 +- src/command/commit.rs | 4 +- src/command/compose_attach.rs | 8 +- src/command/compose_build.rs | 8 +- src/command/compose_create.rs | 8 +- src/command/compose_down.rs | 8 +- src/command/compose_exec.rs | 8 +- src/command/compose_kill.rs | 8 +- src/command/compose_logs.rs | 8 +- src/command/compose_ls.rs | 8 +- src/command/compose_pause.rs | 8 +- src/command/compose_ps.rs | 8 +- src/command/compose_restart.rs | 8 +- src/command/compose_rm.rs | 8 +- src/command/compose_run.rs | 8 +- src/command/compose_start.rs | 8 +- src/command/compose_stop.rs | 8 +- src/command/compose_unpause.rs | 8 +- src/command/compose_up.rs | 10 +- src/command/container_prune.rs | 4 +- src/command/context/create.rs | 4 +- src/command/context/inspect.rs | 4 +- src/command/context/ls.rs | 4 +- src/command/context/rm.rs | 4 +- src/command/context/update.rs | 4 +- src/command/context/use_context.rs | 4 +- src/command/cp.rs | 4 +- src/command/create.rs | 4 +- src/command/diff.rs | 4 +- src/command/events.rs | 4 +- src/command/exec.rs | 4 +- src/command/export.rs | 4 +- src/command/history.rs | 4 +- src/command/image_prune.rs | 4 +- src/command/images.rs | 4 +- src/command/import.rs | 4 +- src/command/info.rs | 4 +- src/command/init.rs | 6 +- src/command/inspect.rs | 4 +- src/command/kill.rs | 4 +- src/command/load.rs | 4 +- src/command/login.rs | 4 +- src/command/logout.rs | 4 +- src/command/logs.rs | 4 +- src/command/network/connect.rs | 4 +- src/command/network/create.rs | 4 +- src/command/network/disconnect.rs | 4 +- src/command/network/inspect.rs | 4 +- src/command/network/ls.rs | 4 +- src/command/network/prune.rs | 4 +- src/command/network/rm.rs | 4 +- src/command/pause.rs | 4 +- src/command/port.rs | 4 +- src/command/ps.rs | 4 +- src/command/pull.rs | 4 +- src/command/push.rs | 4 +- src/command/rename.rs | 4 +- src/command/restart.rs | 4 +- src/command/rm.rs | 4 +- src/command/rmi.rs | 4 +- src/command/run.rs | 4 +- src/command/save.rs | 4 +- src/command/search.rs | 4 +- src/command/start.rs | 4 +- src/command/stats.rs | 4 +- src/command/stop.rs | 4 +- src/command/system/df.rs | 4 +- src/command/system/prune.rs | 4 +- src/command/tag.rs | 4 +- src/command/top.rs | 4 +- src/command/unpause.rs | 4 +- src/command/update.rs | 4 +- src/command/version.rs | 4 +- src/command/volume.rs | 20 +-- src/command/wait.rs | 4 +- 80 files changed, 339 insertions(+), 350 deletions(-) diff --git a/src/command.rs b/src/command.rs index 431a94db..049b3b6f 100644 --- a/src/command.rs +++ b/src/command.rs @@ -81,112 +81,100 @@ pub mod version; pub mod volume; pub mod wait; -/// Unified trait for all Docker commands (both regular and compose) +/// Unified trait for all Docker commands (both regular and compose). #[async_trait] pub trait DockerCommand { - /// The output type this command produces + /// The output type this command produces. type Output; - /// Get the command executor for extensibility - fn get_executor(&self) -> &CommandExecutor; + /// Gets the command name (e.g., "compose", "pull") + fn command_name() -> &'static str; - /// Get mutable command executor for extensibility - fn get_executor_mut(&mut self) -> &mut CommandExecutor; + /// Gets the command executor for extensibility. + fn executor(&self) -> &CommandExecutor; - /// Build the complete command arguments including subcommands + /// Gets the mutable command executor for extensibility. + fn executor_mut(&mut self) -> &mut CommandExecutor; + + /// Builds the complete command arguments including subcommands. fn build_command_args(&self) -> Vec; - /// Execute the command and return the typed output + /// Executes the command and return the typed output. async fn execute(&self) -> Result; - /// Helper method to execute the command with proper error handling + /// Executes the command using the executor. async fn execute_command(&self, command_args: Vec) -> Result { - let executor = self.get_executor(); - - // For compose commands, we need to handle "docker compose " - // For regular commands, we handle "docker " - if command_args.first() == Some(&"compose".to_string()) { - // This is a compose command - args are already formatted correctly - executor.execute_command("docker", command_args).await - } else { - // Regular docker command - first arg is the command name - let command_name = command_args - .first() - .unwrap_or(&"docker".to_string()) - .clone(); - let remaining_args = command_args.iter().skip(1).cloned().collect(); - executor - .execute_command(&command_name, remaining_args) - .await - } + self.executor() + .execute_command(Self::command_name(), command_args) + .await } - /// Add a raw argument to the command (escape hatch) + /// Adds a raw argument to the command (escape hatch). fn arg>(&mut self, arg: S) -> &mut Self { - self.get_executor_mut().add_arg(arg); + self.executor_mut().add_arg(arg); self } - /// Add multiple raw arguments to the command (escape hatch) + /// Adds multiple raw arguments to the command (escape hatch). fn args(&mut self, args: I) -> &mut Self where I: IntoIterator, S: AsRef, { - self.get_executor_mut().add_args(args); + self.executor_mut().add_args(args); self } - /// Add a flag option (e.g., --detach, --rm) + /// Adds a flag option (e.g., `--detach`, `--rm`). fn flag(&mut self, flag: &str) -> &mut Self { - self.get_executor_mut().add_flag(flag); + self.executor_mut().add_flag(flag); self } - /// Add a key-value option (e.g., --name value, --env key=value) + /// Adds a key-value option (e.g., `--name value`, `--env key=value`). fn option(&mut self, key: &str, value: &str) -> &mut Self { - self.get_executor_mut().add_option(key, value); + self.executor_mut().add_option(key, value); self } } -/// Base configuration for all compose commands +/// Base configuration for all compose commands. #[derive(Debug, Clone, Default)] pub struct ComposeConfig { - /// Compose file paths (-f, --file) + /// Compose file paths (-f, --file). pub files: Vec, - /// Project name (-p, --project-name) + /// Project name (-p, --project-name). pub project_name: Option, - /// Project directory (--project-directory) + /// Project directory (--project-directory). pub project_directory: Option, - /// Profiles to enable (--profile) + /// Profiles to enable (--profile). pub profiles: Vec, - /// Environment file (--env-file) + /// Environment file (--env-file). pub env_file: Option, - /// Run in compatibility mode + /// Run in compatibility mode. pub compatibility: bool, - /// Execute in dry run mode + /// Execute in dry run mode. pub dry_run: bool, - /// Progress output type + /// Progress output type. pub progress: Option, - /// ANSI control characters + /// ANSI control characters. pub ansi: Option, - /// Max parallelism (-1 for unlimited) + /// Max parallelism (-1 for unlimited). pub parallel: Option, } -/// Progress output type for compose commands +/// Progress output type for compose commands. #[derive(Debug, Clone, Copy)] pub enum ProgressType { - /// Auto-detect + /// Auto-detects progress output. Auto, - /// TTY output + /// TTY output. Tty, - /// Plain text output + /// Plain text output. Plain, - /// JSON output + /// JSON output. Json, - /// Quiet mode + /// Quiet mode. Quiet, } @@ -202,14 +190,14 @@ impl std::fmt::Display for ProgressType { } } -/// ANSI control character mode +/// ANSI control character mode. #[derive(Debug, Clone, Copy)] pub enum AnsiMode { - /// Never print ANSI + /// Never prints ANSI. Never, - /// Always print ANSI + /// Always prints ANSI. Always, - /// Auto-detect + /// Auto-detects ANSI. Auto, } @@ -224,118 +212,118 @@ impl std::fmt::Display for AnsiMode { } impl ComposeConfig { - /// Create a new compose configuration + /// Creates a new compose configuration. #[must_use] pub fn new() -> Self { Self::default() } - /// Add a compose file + /// Adds a compose file. #[must_use] pub fn file(mut self, path: impl Into) -> Self { self.files.push(path.into()); self } - /// Set project name + /// Sets the project name. #[must_use] pub fn project_name(mut self, name: impl Into) -> Self { self.project_name = Some(name.into()); self } - /// Set project directory + /// Sets the project directory. #[must_use] pub fn project_directory(mut self, dir: impl Into) -> Self { self.project_directory = Some(dir.into()); self } - /// Add a profile + /// Adds a profile. #[must_use] pub fn profile(mut self, profile: impl Into) -> Self { self.profiles.push(profile.into()); self } - /// Set environment file + /// Sets environment file. #[must_use] pub fn env_file(mut self, path: impl Into) -> Self { self.env_file = Some(path.into()); self } - /// Enable compatibility mode + /// Enables compatibility mode. #[must_use] pub fn compatibility(mut self) -> Self { self.compatibility = true; self } - /// Enable dry run mode + /// Enables dry run mode. #[must_use] pub fn dry_run(mut self) -> Self { self.dry_run = true; self } - /// Set progress output type + /// Sets progress output type. #[must_use] pub fn progress(mut self, progress: ProgressType) -> Self { self.progress = Some(progress); self } - /// Set ANSI mode + /// Sets ANSI mode. #[must_use] pub fn ansi(mut self, ansi: AnsiMode) -> Self { self.ansi = Some(ansi); self } - /// Set max parallelism + /// Sets max parallelism. #[must_use] pub fn parallel(mut self, parallel: i32) -> Self { self.parallel = Some(parallel); self } - /// Build global compose arguments + /// Builds global compose arguments. #[must_use] pub fn build_global_args(&self) -> Vec { let mut args = Vec::new(); - // Add compose files + // Adds compose files. for file in &self.files { args.push("--file".to_string()); args.push(file.to_string_lossy().to_string()); } - // Add project name + // Adds project name. if let Some(ref name) = self.project_name { args.push("--project-name".to_string()); args.push(name.clone()); } - // Add project directory + // Adds project directory. if let Some(ref dir) = self.project_directory { args.push("--project-directory".to_string()); args.push(dir.to_string_lossy().to_string()); } - // Add profiles + // Adds profiles. for profile in &self.profiles { args.push("--profile".to_string()); args.push(profile.clone()); } - // Add environment file + // Adds environment file. if let Some(ref env_file) = self.env_file { args.push("--env-file".to_string()); args.push(env_file.to_string_lossy().to_string()); } - // Add flags + // Adds flags. if self.compatibility { args.push("--compatibility".to_string()); } @@ -344,19 +332,19 @@ impl ComposeConfig { args.push("--dry-run".to_string()); } - // Add progress type + // Adds progress type. if let Some(progress) = self.progress { args.push("--progress".to_string()); args.push(progress.to_string()); } - // Add ANSI mode + // Adds ANSI mode. if let Some(ansi) = self.ansi { args.push("--ansi".to_string()); args.push(ansi.to_string()); } - // Add parallel limit + // Adds parallel limit. if let Some(parallel) = self.parallel { args.push("--parallel".to_string()); args.push(parallel.to_string()); @@ -366,72 +354,72 @@ impl ComposeConfig { } } -/// Extended trait for Docker Compose commands +/// Extended trait for Docker Compose commands. pub trait ComposeCommand: DockerCommand { - /// Get the compose configuration - fn get_config(&self) -> &ComposeConfig; + /// Gets the compose configuration. + fn config(&self) -> &ComposeConfig; - /// Get mutable compose configuration for builder pattern - fn get_config_mut(&mut self) -> &mut ComposeConfig; + /// Gets the mutable compose configuration for builder pattern. + fn config_mut(&mut self) -> &mut ComposeConfig; - /// Get the compose subcommand name (e.g., "up", "down", "ps") + /// Gets the compose subcommand name (e.g., `up`, `down`, `ps`). fn subcommand(&self) -> &'static str; - /// Build command-specific arguments (without global compose args) + /// Builds command-specific arguments (without global compose args). fn build_subcommand_args(&self) -> Vec; - /// Build complete command arguments including "compose" and global args\ - /// (This provides the implementation for `DockerCommandV2::build_command_args`) + /// Builds complete command arguments including "compose" and global args. + /// This provides the implementation for `DockerCommandV2::build_command_args`. fn build_command_args(&self) -> Vec { let mut args = vec!["compose".to_string()]; - // Add global compose arguments - args.extend(self.get_config().build_global_args()); + // Adds global compose arguments. + args.extend(self.config().build_global_args()); - // Add the subcommand + // Adds the subcommand. args.push(self.subcommand().to_string()); - // Add command-specific arguments + // Adds command-specific arguments. args.extend(self.build_subcommand_args()); - // Add raw arguments from executor - args.extend(self.get_executor().raw_args.clone()); + // Adds raw arguments from executor. + args.extend(self.executor().raw_args.clone()); args } - /// Helper builder methods for common compose config options + /// Helper builder methods for common compose config options. #[must_use] fn file>(mut self, file: P) -> Self where Self: Sized, { - self.get_config_mut().files.push(file.into()); + self.config_mut().files.push(file.into()); self } - /// Set project name for compose command + /// Sets project name for compose command. #[must_use] fn project_name(mut self, name: impl Into) -> Self where Self: Sized, { - self.get_config_mut().project_name = Some(name.into()); + self.config_mut().project_name = Some(name.into()); self } } -/// Common functionality for executing Docker commands +/// Common functionality for executing Docker commands. #[derive(Debug, Clone)] pub struct CommandExecutor { - /// Additional raw arguments added via escape hatch + /// Additional raw arguments added via escape hatch. pub raw_args: Vec, - /// Platform information for runtime abstraction + /// Platform information for runtime abstraction. pub platform_info: Option, } impl CommandExecutor { - /// Create a new command executor + /// Creates a new command executor. #[must_use] pub fn new() -> Self { Self { @@ -440,11 +428,11 @@ impl CommandExecutor { } } - /// Create a new command executor with platform detection + /// Creates a new command executor with platform detection. /// /// # Errors /// - /// Returns an error if platform detection fails + /// Returns an error if platform detection fails. pub fn with_platform() -> Result { let platform_info = PlatformInfo::detect()?; Ok(Self { @@ -453,15 +441,15 @@ impl CommandExecutor { }) } - /// Set the platform information + /// Sets the platform information. #[must_use] pub fn platform(mut self, platform_info: PlatformInfo) -> Self { self.platform_info = Some(platform_info); self } - /// Get the runtime command to use - fn get_runtime_command(&self) -> String { + /// Gets the runtime command to use. + fn runtime_command(&self) -> String { if let Some(ref platform_info) = self.platform_info { platform_info.runtime.command().to_string() } else { @@ -469,26 +457,27 @@ impl CommandExecutor { } } - /// Execute a Docker command with the given arguments + /// Executes a Docker command with the given arguments. /// /// # Errors - /// Returns an error if the Docker command fails to execute or returns a non-zero exit code + /// + /// Returns an error if the Docker command fails to execute or returns a non-zero exit code. pub async fn execute_command( &self, command_name: &str, args: Vec, ) -> Result { - // Prepend raw args (they should come before command-specific args) + // prepends raw args (they should come before command-specific args) let mut all_args = self.raw_args.clone(); all_args.extend(args); - // Insert the command name at the beginning + // inserts the command name at the beginning all_args.insert(0, command_name.to_string()); - let runtime_command = self.get_runtime_command(); + let runtime_command = self.runtime_command(); let mut command = TokioCommand::new(&runtime_command); - // Set environment variables from platform info + // sets environment variables from platform info if let Some(ref platform_info) = self.platform_info { for (key, value) in platform_info.environment_vars() { command.env(key, value); @@ -529,13 +518,13 @@ impl CommandExecutor { }) } - /// Add a raw argument + /// Adds a raw argument. pub fn add_arg>(&mut self, arg: S) { self.raw_args .push(arg.as_ref().to_string_lossy().to_string()); } - /// Add multiple raw arguments + /// Adds multiple raw arguments. pub fn add_args(&mut self, args: I) where I: IntoIterator, @@ -546,7 +535,7 @@ impl CommandExecutor { } } - /// Add a flag option + /// Adds a flag option. pub fn add_flag(&mut self, flag: &str) { let flag_arg = if flag.starts_with('-') { flag.to_string() @@ -558,7 +547,7 @@ impl CommandExecutor { self.raw_args.push(flag_arg); } - /// Add a key-value option + /// Adds a key-value option. pub fn add_option(&mut self, key: &str, value: &str) { let key_arg = if key.starts_with('-') { key.to_string() @@ -578,73 +567,73 @@ impl Default for CommandExecutor { } } -/// Output from executing a Docker command +/// Output from executing a Docker command. #[derive(Debug, Clone)] pub struct CommandOutput { - /// Standard output from the command + /// Standard output from the command. pub stdout: String, - /// Standard error from the command + /// Standard error from the command. pub stderr: String, - /// Exit code + /// Exit code. pub exit_code: i32, - /// Whether the command was successful + /// Whether the command was successful. pub success: bool, } impl CommandOutput { - /// Get stdout lines as a vector + /// Gets stdout lines as a vector. #[must_use] pub fn stdout_lines(&self) -> Vec<&str> { self.stdout.lines().collect() } - /// Get stderr lines as a vector + /// Gets stderr lines as a vector. #[must_use] pub fn stderr_lines(&self) -> Vec<&str> { self.stderr.lines().collect() } - /// Check if stdout is empty + /// Checks if stdout is empty. #[must_use] pub fn stdout_is_empty(&self) -> bool { self.stdout.trim().is_empty() } - /// Check if stderr is empty + /// Checks if stderr is empty. #[must_use] pub fn stderr_is_empty(&self) -> bool { self.stderr.trim().is_empty() } } -/// Helper for building environment variables +/// Helper for building environment variables. #[derive(Debug, Clone, Default)] pub struct EnvironmentBuilder { vars: HashMap, } impl EnvironmentBuilder { - /// Create a new environment builder + /// Creates a new environment builder. #[must_use] pub fn new() -> Self { Self::default() } - /// Add an environment variable + /// Adds an environment variable. #[must_use] pub fn var(mut self, key: impl Into, value: impl Into) -> Self { self.vars.insert(key.into(), value.into()); self } - /// Add multiple environment variables from a `HashMap` + /// Adds multiple environment variables from a [`HashMap`]. #[must_use] pub fn vars(mut self, vars: HashMap) -> Self { self.vars.extend(vars); self } - /// Build the environment arguments for Docker + /// Builds the environment arguments for Docker. #[must_use] pub fn build_args(&self) -> Vec { let mut args = Vec::new(); @@ -655,27 +644,27 @@ impl EnvironmentBuilder { args } - /// Get the environment variables as a `HashMap` + /// Gets the environment variables as a [`HashMap`]. #[must_use] pub fn as_map(&self) -> &HashMap { &self.vars } } -/// Helper for building port mappings +/// Helper for building port mappings. #[derive(Debug, Clone, Default)] pub struct PortBuilder { mappings: Vec, } impl PortBuilder { - /// Create a new port builder + /// Creates a new port builder. #[must_use] pub fn new() -> Self { Self::default() } - /// Add a port mapping + /// Adds a port mapping. #[must_use] pub fn port(mut self, host_port: u16, container_port: u16) -> Self { self.mappings.push(PortMapping { @@ -687,7 +676,7 @@ impl PortBuilder { self } - /// Add a port mapping with protocol + /// Adds a port mapping with protocol. #[must_use] pub fn port_with_protocol( mut self, @@ -704,7 +693,7 @@ impl PortBuilder { self } - /// Add a dynamic port mapping (Docker assigns host port) + /// Adds a dynamic port mapping (Docker assigns host port). #[must_use] pub fn dynamic_port(mut self, container_port: u16) -> Self { self.mappings.push(PortMapping { @@ -716,7 +705,7 @@ impl PortBuilder { self } - /// Build the port arguments for Docker + /// Builds the port arguments for Docker. #[must_use] pub fn build_args(&self) -> Vec { let mut args = Vec::new(); @@ -727,23 +716,23 @@ impl PortBuilder { args } - /// Get the port mappings + /// Gets the port mappings. #[must_use] pub fn mappings(&self) -> &[PortMapping] { &self.mappings } } -/// Port mapping configuration +/// Port mapping configuration. #[derive(Debug, Clone)] pub struct PortMapping { - /// Host port (None for dynamic allocation) + /// Host port (None for dynamic allocation). pub host_port: Option, - /// Container port + /// Container port. pub container_port: u16, - /// Protocol (TCP or UDP) + /// Protocol (TCP or UDP). pub protocol: Protocol, - /// Host IP to bind to (None for all interfaces) + /// Host IP to bind to (None for all interfaces). pub host_ip: Option, } @@ -774,12 +763,12 @@ impl std::fmt::Display for PortMapping { } } -/// Network protocol for port mappings +/// Network protocol for port mappings. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Protocol { - /// TCP protocol + /// TCP protocol. Tcp, - /// UDP protocol + /// UDP protocol. Udp, } diff --git a/src/command/attach.rs b/src/command/attach.rs index 0cd23b67..5d3c260d 100644 --- a/src/command/attach.rs +++ b/src/command/attach.rs @@ -149,11 +149,11 @@ impl AttachCommand { impl DockerCommand for AttachCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/bake.rs b/src/command/bake.rs index 7c845d2f..1d2a59ea 100644 --- a/src/command/bake.rs +++ b/src/command/bake.rs @@ -771,11 +771,11 @@ impl DockerCommand for BakeCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } @@ -940,9 +940,9 @@ mod tests { #[test] fn test_bake_command_extensibility() { let mut bake_cmd = BakeCommand::new(); - bake_cmd.get_executor_mut().add_arg("--experimental"); + bake_cmd.executor_mut().add_arg("--experimental"); bake_cmd - .get_executor_mut() + .executor_mut() .add_args(vec!["--custom", "value"]); // Extensibility is handled through the executor's raw_args diff --git a/src/command/build.rs b/src/command/build.rs index ca8e4e3b..a53e4ce7 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -1296,11 +1296,11 @@ impl BuildCommand { impl DockerCommand for BuildCommand { type Output = BuildOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/builder/build.rs b/src/command/builder/build.rs index 426a780b..483ce56e 100644 --- a/src/command/builder/build.rs +++ b/src/command/builder/build.rs @@ -141,11 +141,11 @@ impl BuilderBuildCommand { impl DockerCommand for BuilderBuildCommand { type Output = BuildOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.inner.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.inner.executor } diff --git a/src/command/builder/prune.rs b/src/command/builder/prune.rs index f253f0f7..0bc6b16b 100644 --- a/src/command/builder/prune.rs +++ b/src/command/builder/prune.rs @@ -154,11 +154,11 @@ impl Default for BuilderPruneCommand { impl DockerCommand for BuilderPruneCommand { type Output = BuilderPruneResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -298,7 +298,7 @@ Total reclaimed space: 2.5GB"; #[test] fn test_builder_prune_extensibility() { let mut cmd = BuilderPruneCommand::new(); - cmd.get_executor_mut() + cmd.executor_mut() .raw_args .push("--custom-flag".to_string()); diff --git a/src/command/commit.rs b/src/command/commit.rs index 507fc27e..fd269304 100644 --- a/src/command/commit.rs +++ b/src/command/commit.rs @@ -232,11 +232,11 @@ impl DockerCommand for CommitCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/compose_attach.rs b/src/command/compose_attach.rs index 749bb799..07d739de 100644 --- a/src/command/compose_attach.rs +++ b/src/command/compose_attach.rs @@ -80,11 +80,11 @@ impl ComposeAttachCommand { impl DockerCommand for ComposeAttachCommand { type Output = AttachResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -105,11 +105,11 @@ impl DockerCommand for ComposeAttachCommand { } impl ComposeCommand for ComposeAttachCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_build.rs b/src/command/compose_build.rs index bb326b70..b6a0eccc 100644 --- a/src/command/compose_build.rs +++ b/src/command/compose_build.rs @@ -178,11 +178,11 @@ impl Default for ComposeBuildCommand { impl DockerCommand for ComposeBuildCommand { type Output = ComposeBuildResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -205,11 +205,11 @@ impl DockerCommand for ComposeBuildCommand { } impl ComposeCommand for ComposeBuildCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_create.rs b/src/command/compose_create.rs index 43c5c36e..d50e8db8 100644 --- a/src/command/compose_create.rs +++ b/src/command/compose_create.rs @@ -153,11 +153,11 @@ impl Default for ComposeCreateCommand { impl DockerCommand for ComposeCreateCommand { type Output = ComposeCreateResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -180,11 +180,11 @@ impl DockerCommand for ComposeCreateCommand { } impl ComposeCommand for ComposeCreateCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_down.rs b/src/command/compose_down.rs index 25d7cbf5..18ff9e50 100644 --- a/src/command/compose_down.rs +++ b/src/command/compose_down.rs @@ -129,11 +129,11 @@ impl Default for ComposeDownCommand { impl DockerCommand for ComposeDownCommand { type Output = ComposeDownResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -157,11 +157,11 @@ impl DockerCommand for ComposeDownCommand { } impl ComposeCommand for ComposeDownCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_exec.rs b/src/command/compose_exec.rs index eb092181..2212c7e6 100644 --- a/src/command/compose_exec.rs +++ b/src/command/compose_exec.rs @@ -169,11 +169,11 @@ impl ComposeExecCommand { impl DockerCommand for ComposeExecCommand { type Output = ComposeExecResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -198,11 +198,11 @@ impl DockerCommand for ComposeExecCommand { } impl ComposeCommand for ComposeExecCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_kill.rs b/src/command/compose_kill.rs index fd05a4e4..2a64552f 100644 --- a/src/command/compose_kill.rs +++ b/src/command/compose_kill.rs @@ -78,11 +78,11 @@ impl Default for ComposeKillCommand { impl DockerCommand for ComposeKillCommand { type Output = ComposeKillResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -105,11 +105,11 @@ impl DockerCommand for ComposeKillCommand { } impl ComposeCommand for ComposeKillCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_logs.rs b/src/command/compose_logs.rs index 8a8daea0..cb75c3f2 100644 --- a/src/command/compose_logs.rs +++ b/src/command/compose_logs.rs @@ -139,11 +139,11 @@ impl Default for ComposeLogsCommand { impl DockerCommand for ComposeLogsCommand { type Output = ComposeLogsResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -166,11 +166,11 @@ impl DockerCommand for ComposeLogsCommand { } impl ComposeCommand for ComposeLogsCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_ls.rs b/src/command/compose_ls.rs index 95650b85..020eb1c8 100644 --- a/src/command/compose_ls.rs +++ b/src/command/compose_ls.rs @@ -120,11 +120,11 @@ impl ComposeLsCommand { impl DockerCommand for ComposeLsCommand { type Output = LsResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -152,11 +152,11 @@ impl DockerCommand for ComposeLsCommand { } impl ComposeCommand for ComposeLsCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_pause.rs b/src/command/compose_pause.rs index 030c7124..91a09464 100644 --- a/src/command/compose_pause.rs +++ b/src/command/compose_pause.rs @@ -68,11 +68,11 @@ impl Default for ComposePauseCommand { impl DockerCommand for ComposePauseCommand { type Output = ComposePauseResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -95,11 +95,11 @@ impl DockerCommand for ComposePauseCommand { } impl ComposeCommand for ComposePauseCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_ps.rs b/src/command/compose_ps.rs index fc0ef78b..a0fdf202 100644 --- a/src/command/compose_ps.rs +++ b/src/command/compose_ps.rs @@ -198,11 +198,11 @@ impl Default for ComposePsCommand { impl DockerCommand for ComposePsCommand { type Output = ComposePsResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -232,11 +232,11 @@ impl DockerCommand for ComposePsCommand { } impl ComposeCommand for ComposePsCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_restart.rs b/src/command/compose_restart.rs index 1d555f8c..1db4c9e8 100644 --- a/src/command/compose_restart.rs +++ b/src/command/compose_restart.rs @@ -79,11 +79,11 @@ impl Default for ComposeRestartCommand { impl DockerCommand for ComposeRestartCommand { type Output = ComposeRestartResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -106,11 +106,11 @@ impl DockerCommand for ComposeRestartCommand { } impl ComposeCommand for ComposeRestartCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_rm.rs b/src/command/compose_rm.rs index 5002b923..ce6ada4a 100644 --- a/src/command/compose_rm.rs +++ b/src/command/compose_rm.rs @@ -101,11 +101,11 @@ impl Default for ComposeRmCommand { impl DockerCommand for ComposeRmCommand { type Output = ComposeRmResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -129,11 +129,11 @@ impl DockerCommand for ComposeRmCommand { } impl ComposeCommand for ComposeRmCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_run.rs b/src/command/compose_run.rs index de3ef3c9..1fc51625 100644 --- a/src/command/compose_run.rs +++ b/src/command/compose_run.rs @@ -236,11 +236,11 @@ impl ComposeRunCommand { impl DockerCommand for ComposeRunCommand { type Output = ComposeRunResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -265,11 +265,11 @@ impl DockerCommand for ComposeRunCommand { } impl ComposeCommand for ComposeRunCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_start.rs b/src/command/compose_start.rs index 8afc4ec6..e96e0142 100644 --- a/src/command/compose_start.rs +++ b/src/command/compose_start.rs @@ -68,11 +68,11 @@ impl Default for ComposeStartCommand { impl DockerCommand for ComposeStartCommand { type Output = ComposeStartResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -95,11 +95,11 @@ impl DockerCommand for ComposeStartCommand { } impl ComposeCommand for ComposeStartCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_stop.rs b/src/command/compose_stop.rs index d29e9698..d1c52066 100644 --- a/src/command/compose_stop.rs +++ b/src/command/compose_stop.rs @@ -79,11 +79,11 @@ impl Default for ComposeStopCommand { impl DockerCommand for ComposeStopCommand { type Output = ComposeStopResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -106,11 +106,11 @@ impl DockerCommand for ComposeStopCommand { } impl ComposeCommand for ComposeStopCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_unpause.rs b/src/command/compose_unpause.rs index e1a61bed..180206a2 100644 --- a/src/command/compose_unpause.rs +++ b/src/command/compose_unpause.rs @@ -68,11 +68,11 @@ impl Default for ComposeUnpauseCommand { impl DockerCommand for ComposeUnpauseCommand { type Output = ComposeUnpauseResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -95,11 +95,11 @@ impl DockerCommand for ComposeUnpauseCommand { } impl ComposeCommand for ComposeUnpauseCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/compose_up.rs b/src/command/compose_up.rs index 05e86d95..955cf807 100644 --- a/src/command/compose_up.rs +++ b/src/command/compose_up.rs @@ -79,7 +79,7 @@ impl std::fmt::Display for PullPolicy { pub struct ComposeUpResult { /// Raw stdout output pub stdout: String, - /// Raw stderr output + /// Raw stderr output pub stderr: String, /// Success status pub success: bool, @@ -273,11 +273,11 @@ impl Default for ComposeUpCommand { impl DockerCommand for ComposeUpCommand { type Output = ComposeUpResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -301,11 +301,11 @@ impl DockerCommand for ComposeUpCommand { } impl ComposeCommand for ComposeUpCommand { - fn get_config(&self) -> &ComposeConfig { + fn config(&self) -> &ComposeConfig { &self.config } - fn get_config_mut(&mut self) -> &mut ComposeConfig { + fn config_mut(&mut self) -> &mut ComposeConfig { &mut self.config } diff --git a/src/command/container_prune.rs b/src/command/container_prune.rs index e898d8bf..56b78cc8 100644 --- a/src/command/container_prune.rs +++ b/src/command/container_prune.rs @@ -119,11 +119,11 @@ impl DockerCommand for ContainerPruneCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/context/create.rs b/src/command/context/create.rs index 4ab66017..ceb5e5a2 100644 --- a/src/command/context/create.rs +++ b/src/command/context/create.rs @@ -136,11 +136,11 @@ impl ContextCreateCommand { impl DockerCommand for ContextCreateCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/context/inspect.rs b/src/command/context/inspect.rs index fe26754a..e0392609 100644 --- a/src/command/context/inspect.rs +++ b/src/command/context/inspect.rs @@ -63,11 +63,11 @@ impl ContextInspectCommand { impl DockerCommand for ContextInspectCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/context/ls.rs b/src/command/context/ls.rs index d4aa4e46..6a7944fc 100644 --- a/src/command/context/ls.rs +++ b/src/command/context/ls.rs @@ -98,11 +98,11 @@ impl Default for ContextLsCommand { impl DockerCommand for ContextLsCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/context/rm.rs b/src/command/context/rm.rs index 9f0ad225..9168f910 100644 --- a/src/command/context/rm.rs +++ b/src/command/context/rm.rs @@ -62,11 +62,11 @@ impl ContextRmCommand { impl DockerCommand for ContextRmCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/context/update.rs b/src/command/context/update.rs index ef8b7d5a..9d8d315e 100644 --- a/src/command/context/update.rs +++ b/src/command/context/update.rs @@ -125,11 +125,11 @@ impl ContextUpdateCommand { impl DockerCommand for ContextUpdateCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/context/use_context.rs b/src/command/context/use_context.rs index 595fccad..5ee81ec8 100644 --- a/src/command/context/use_context.rs +++ b/src/command/context/use_context.rs @@ -44,11 +44,11 @@ impl ContextUseCommand { impl DockerCommand for ContextUseCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/cp.rs b/src/command/cp.rs index 55019d2b..3eb0de44 100644 --- a/src/command/cp.rs +++ b/src/command/cp.rs @@ -189,11 +189,11 @@ impl DockerCommand for CpCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/create.rs b/src/command/create.rs index 77ab1615..93e30049 100644 --- a/src/command/create.rs +++ b/src/command/create.rs @@ -314,11 +314,11 @@ impl CreateCommand { impl DockerCommand for CreateCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/diff.rs b/src/command/diff.rs index 976fcb65..4da543ee 100644 --- a/src/command/diff.rs +++ b/src/command/diff.rs @@ -136,11 +136,11 @@ impl DockerCommand for DiffCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/events.rs b/src/command/events.rs index 7fd791cf..270b86e6 100644 --- a/src/command/events.rs +++ b/src/command/events.rs @@ -231,11 +231,11 @@ impl DockerCommand for EventsCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } diff --git a/src/command/exec.rs b/src/command/exec.rs index c00e1825..6d8a5fb7 100644 --- a/src/command/exec.rs +++ b/src/command/exec.rs @@ -297,11 +297,11 @@ impl ExecCommand { impl DockerCommand for ExecCommand { type Output = ExecOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/export.rs b/src/command/export.rs index a4c5b053..4d038092 100644 --- a/src/command/export.rs +++ b/src/command/export.rs @@ -128,11 +128,11 @@ impl DockerCommand for ExportCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/history.rs b/src/command/history.rs index a2922e22..feea45c4 100644 --- a/src/command/history.rs +++ b/src/command/history.rs @@ -270,11 +270,11 @@ impl DockerCommand for HistoryCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } diff --git a/src/command/image_prune.rs b/src/command/image_prune.rs index 5b34b35b..92da25a5 100644 --- a/src/command/image_prune.rs +++ b/src/command/image_prune.rs @@ -154,11 +154,11 @@ impl DockerCommand for ImagePruneCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/images.rs b/src/command/images.rs index fc3a7f6c..fe5f9d81 100644 --- a/src/command/images.rs +++ b/src/command/images.rs @@ -806,11 +806,11 @@ impl ImagesOutput { impl DockerCommand for ImagesCommand { type Output = ImagesOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/import.rs b/src/command/import.rs index 0b41e170..35bb2ac1 100644 --- a/src/command/import.rs +++ b/src/command/import.rs @@ -240,11 +240,11 @@ impl DockerCommand for ImportCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } diff --git a/src/command/info.rs b/src/command/info.rs index 00076f8e..f9b17b3c 100644 --- a/src/command/info.rs +++ b/src/command/info.rs @@ -533,11 +533,11 @@ impl InfoOutput { impl DockerCommand for InfoCommand { type Output = InfoOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/init.rs b/src/command/init.rs index 12fbfd94..b64a6298 100644 --- a/src/command/init.rs +++ b/src/command/init.rs @@ -121,11 +121,11 @@ impl Default for InitCommand { impl DockerCommand for InitCommand { type Output = InitOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -317,7 +317,7 @@ mod tests { let mut cmd = InitCommand::new(); // Test that we can add custom raw arguments - cmd.get_executor_mut() + cmd.executor_mut() .raw_args .push("--custom-flag".to_string()); diff --git a/src/command/inspect.rs b/src/command/inspect.rs index 50208d62..0f532e9f 100644 --- a/src/command/inspect.rs +++ b/src/command/inspect.rs @@ -180,11 +180,11 @@ impl InspectCommand { impl DockerCommand for InspectCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/kill.rs b/src/command/kill.rs index 1a7334c4..3cba2c6c 100644 --- a/src/command/kill.rs +++ b/src/command/kill.rs @@ -140,11 +140,11 @@ impl DockerCommand for KillCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/load.rs b/src/command/load.rs index 92e2458e..66352ac3 100644 --- a/src/command/load.rs +++ b/src/command/load.rs @@ -174,11 +174,11 @@ impl DockerCommand for LoadCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/login.rs b/src/command/login.rs index 5550163f..70c9ac07 100644 --- a/src/command/login.rs +++ b/src/command/login.rs @@ -198,11 +198,11 @@ impl LoginOutput { impl DockerCommand for LoginCommand { type Output = LoginOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/logout.rs b/src/command/logout.rs index b8338d65..182a2b93 100644 --- a/src/command/logout.rs +++ b/src/command/logout.rs @@ -162,11 +162,11 @@ impl LogoutOutput { impl DockerCommand for LogoutCommand { type Output = LogoutOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/logs.rs b/src/command/logs.rs index 48f16829..9d7ee5a5 100644 --- a/src/command/logs.rs +++ b/src/command/logs.rs @@ -111,11 +111,11 @@ impl LogsCommand { impl DockerCommand for LogsCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/connect.rs b/src/command/network/connect.rs index f9c549f8..a13323eb 100644 --- a/src/command/network/connect.rs +++ b/src/command/network/connect.rs @@ -139,11 +139,11 @@ impl DockerCommand for NetworkConnectCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/create.rs b/src/command/network/create.rs index 3c862a18..01208314 100644 --- a/src/command/network/create.rs +++ b/src/command/network/create.rs @@ -252,11 +252,11 @@ impl DockerCommand for NetworkCreateCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/disconnect.rs b/src/command/network/disconnect.rs index 33317d01..52af087e 100644 --- a/src/command/network/disconnect.rs +++ b/src/command/network/disconnect.rs @@ -63,11 +63,11 @@ impl DockerCommand for NetworkDisconnectCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/inspect.rs b/src/command/network/inspect.rs index b30d1bb5..a77286cd 100644 --- a/src/command/network/inspect.rs +++ b/src/command/network/inspect.rs @@ -96,11 +96,11 @@ impl DockerCommand for NetworkInspectCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/ls.rs b/src/command/network/ls.rs index 3388ce86..346f4e17 100644 --- a/src/command/network/ls.rs +++ b/src/command/network/ls.rs @@ -149,11 +149,11 @@ impl DockerCommand for NetworkLsCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/prune.rs b/src/command/network/prune.rs index 31280ae7..fed5bb75 100644 --- a/src/command/network/prune.rs +++ b/src/command/network/prune.rs @@ -98,11 +98,11 @@ impl DockerCommand for NetworkPruneCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/network/rm.rs b/src/command/network/rm.rs index c2e53a28..93e237eb 100644 --- a/src/command/network/rm.rs +++ b/src/command/network/rm.rs @@ -79,11 +79,11 @@ impl DockerCommand for NetworkRmCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/pause.rs b/src/command/pause.rs index 7104c1ff..92c07fba 100644 --- a/src/command/pause.rs +++ b/src/command/pause.rs @@ -115,11 +115,11 @@ impl PauseCommand { impl DockerCommand for PauseCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/port.rs b/src/command/port.rs index 52d02cf8..bc3c6988 100644 --- a/src/command/port.rs +++ b/src/command/port.rs @@ -160,11 +160,11 @@ impl DockerCommand for PortCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/ps.rs b/src/command/ps.rs index fd1cb4a2..6194f59e 100644 --- a/src/command/ps.rs +++ b/src/command/ps.rs @@ -443,11 +443,11 @@ impl Default for PsCommand { impl DockerCommand for PsCommand { type Output = PsOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/pull.rs b/src/command/pull.rs index 028789f9..89aed16c 100644 --- a/src/command/pull.rs +++ b/src/command/pull.rs @@ -306,11 +306,11 @@ impl Default for PullCommand { impl DockerCommand for PullCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/push.rs b/src/command/push.rs index 6de8e2da..a106a66b 100644 --- a/src/command/push.rs +++ b/src/command/push.rs @@ -314,11 +314,11 @@ impl Default for PushCommand { impl DockerCommand for PushCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/rename.rs b/src/command/rename.rs index 56cd3cdf..ddafb876 100644 --- a/src/command/rename.rs +++ b/src/command/rename.rs @@ -97,11 +97,11 @@ impl RenameCommand { impl DockerCommand for RenameCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/restart.rs b/src/command/restart.rs index bcc774d9..4781298b 100644 --- a/src/command/restart.rs +++ b/src/command/restart.rs @@ -134,11 +134,11 @@ impl RestartCommand { impl DockerCommand for RestartCommand { type Output = RestartResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/rm.rs b/src/command/rm.rs index c8674179..70f4b3bb 100644 --- a/src/command/rm.rs +++ b/src/command/rm.rs @@ -148,11 +148,11 @@ impl RmCommand { impl DockerCommand for RmCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/rmi.rs b/src/command/rmi.rs index f1173975..eb695688 100644 --- a/src/command/rmi.rs +++ b/src/command/rmi.rs @@ -189,11 +189,11 @@ impl DockerCommand for RmiCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/run.rs b/src/command/run.rs index 1944be8b..14532c9c 100644 --- a/src/command/run.rs +++ b/src/command/run.rs @@ -1333,11 +1333,11 @@ impl RunCommand { impl DockerCommand for RunCommand { type Output = ContainerId; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/save.rs b/src/command/save.rs index 05a17cdd..3b7d1ff1 100644 --- a/src/command/save.rs +++ b/src/command/save.rs @@ -158,11 +158,11 @@ impl DockerCommand for SaveCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/search.rs b/src/command/search.rs index c905ec51..17ab87b3 100644 --- a/src/command/search.rs +++ b/src/command/search.rs @@ -474,11 +474,11 @@ impl SearchOutput { impl DockerCommand for SearchCommand { type Output = SearchOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/start.rs b/src/command/start.rs index aae4865d..572e8cac 100644 --- a/src/command/start.rs +++ b/src/command/start.rs @@ -190,11 +190,11 @@ impl StartCommand { impl DockerCommand for StartCommand { type Output = StartResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/stats.rs b/src/command/stats.rs index 30db22e6..f3a2aa91 100644 --- a/src/command/stats.rs +++ b/src/command/stats.rs @@ -232,11 +232,11 @@ impl Default for StatsCommand { impl DockerCommand for StatsCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/stop.rs b/src/command/stop.rs index 01ff4743..dcf8ae64 100644 --- a/src/command/stop.rs +++ b/src/command/stop.rs @@ -134,11 +134,11 @@ impl StopCommand { impl DockerCommand for StopCommand { type Output = StopResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/system/df.rs b/src/command/system/df.rs index 1fbacd04..e8363ef8 100644 --- a/src/command/system/df.rs +++ b/src/command/system/df.rs @@ -337,11 +337,11 @@ impl DockerCommand for SystemDfCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/system/prune.rs b/src/command/system/prune.rs index 87da427a..fe9a5586 100644 --- a/src/command/system/prune.rs +++ b/src/command/system/prune.rs @@ -158,11 +158,11 @@ impl DockerCommand for SystemPruneCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/tag.rs b/src/command/tag.rs index 4daa4ed6..03729988 100644 --- a/src/command/tag.rs +++ b/src/command/tag.rs @@ -111,11 +111,11 @@ impl TagCommand { impl DockerCommand for TagCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/top.rs b/src/command/top.rs index 15864094..6055d277 100644 --- a/src/command/top.rs +++ b/src/command/top.rs @@ -197,11 +197,11 @@ impl TopCommand { impl DockerCommand for TopCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/unpause.rs b/src/command/unpause.rs index fb703e20..c2ecdcef 100644 --- a/src/command/unpause.rs +++ b/src/command/unpause.rs @@ -115,11 +115,11 @@ impl UnpauseCommand { impl DockerCommand for UnpauseCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/update.rs b/src/command/update.rs index 3de25813..720ba1ff 100644 --- a/src/command/update.rs +++ b/src/command/update.rs @@ -451,11 +451,11 @@ impl DockerCommand for UpdateCommand { args } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/version.rs b/src/command/version.rs index 409b5808..be14dc4d 100644 --- a/src/command/version.rs +++ b/src/command/version.rs @@ -398,11 +398,11 @@ impl VersionOutput { impl DockerCommand for VersionCommand { type Output = VersionOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } diff --git a/src/command/volume.rs b/src/command/volume.rs index a41ab77c..c9f45271 100644 --- a/src/command/volume.rs +++ b/src/command/volume.rs @@ -116,11 +116,11 @@ impl DockerCommand for VolumeCreateCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } @@ -236,11 +236,11 @@ impl DockerCommand for VolumeLsCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } @@ -362,11 +362,11 @@ impl DockerCommand for VolumeRmCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } @@ -462,11 +462,11 @@ impl DockerCommand for VolumeInspectCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } @@ -582,11 +582,11 @@ impl DockerCommand for VolumePruneCommand { .await } - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } diff --git a/src/command/wait.rs b/src/command/wait.rs index b6916caf..9a4e4998 100644 --- a/src/command/wait.rs +++ b/src/command/wait.rs @@ -129,11 +129,11 @@ impl WaitCommand { impl DockerCommand for WaitCommand { type Output = CommandOutput; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } From 9ca97c85f3cfa0f76791d917c8667073d0f9780f Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 12:14:34 +0800 Subject: [PATCH 3/9] clean: remove old compose commands --- src/compose/attach.rs | 179 --------------- src/compose/build.rs | 107 --------- src/compose/config.rs | 288 ------------------------ src/compose/convert.rs | 327 ---------------------------- src/compose/cp.rs | 202 ----------------- src/compose/create.rs | 248 --------------------- src/compose/down.rs | 281 ------------------------ src/compose/events.rs | 212 ------------------ src/compose/exec.rs | 121 ----------- src/compose/images.rs | 239 -------------------- src/compose/kill.rs | 162 -------------- src/compose/logs.rs | 277 ----------------------- src/compose/ls.rs | 256 ---------------------- src/compose/pause.rs | 121 ----------- src/compose/port.rs | 191 ---------------- src/compose/ps.rs | 361 ------------------------------ src/compose/push.rs | 171 --------------- src/compose/restart.rs | 149 ------------- src/compose/rm.rs | 188 ---------------- src/compose/run.rs | 116 ---------- src/compose/scale.rs | 157 -------------- src/compose/start.rs | 131 ----------- src/compose/stop.rs | 156 ------------- src/compose/top.rs | 121 ----------- src/compose/unpause.rs | 123 ----------- src/compose/up.rs | 483 ----------------------------------------- src/compose/version.rs | 163 -------------- src/compose/wait.rs | 190 ---------------- src/compose/watch.rs | 142 ------------ 29 files changed, 5862 deletions(-) delete mode 100644 src/compose/attach.rs delete mode 100644 src/compose/build.rs delete mode 100644 src/compose/config.rs delete mode 100644 src/compose/convert.rs delete mode 100644 src/compose/cp.rs delete mode 100644 src/compose/create.rs delete mode 100644 src/compose/down.rs delete mode 100644 src/compose/events.rs delete mode 100644 src/compose/exec.rs delete mode 100644 src/compose/images.rs delete mode 100644 src/compose/kill.rs delete mode 100644 src/compose/logs.rs delete mode 100644 src/compose/ls.rs delete mode 100644 src/compose/pause.rs delete mode 100644 src/compose/port.rs delete mode 100644 src/compose/ps.rs delete mode 100644 src/compose/push.rs delete mode 100644 src/compose/restart.rs delete mode 100644 src/compose/rm.rs delete mode 100644 src/compose/run.rs delete mode 100644 src/compose/scale.rs delete mode 100644 src/compose/start.rs delete mode 100644 src/compose/stop.rs delete mode 100644 src/compose/top.rs delete mode 100644 src/compose/unpause.rs delete mode 100644 src/compose/up.rs delete mode 100644 src/compose/version.rs delete mode 100644 src/compose/wait.rs delete mode 100644 src/compose/watch.rs diff --git a/src/compose/attach.rs b/src/compose/attach.rs deleted file mode 100644 index 4f8a82b3..00000000 --- a/src/compose/attach.rs +++ /dev/null @@ -1,179 +0,0 @@ -//! Docker Compose attach command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose attach command -/// -/// Attach to a running container's output. -#[derive(Debug, Clone, Default)] -pub struct ComposeAttachCommand { - /// Base configuration - pub config: ComposeConfig, - /// Service to attach to - pub service: String, - /// Detach keys sequence - pub detach_keys: Option, - /// Container index if service has multiple instances - pub index: Option, - /// Don't stream STDIN - pub no_stdin: bool, - /// Use a pseudo-TTY - pub sig_proxy: bool, -} - -/// Result from attach command -#[derive(Debug, Clone)] -pub struct AttachResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeAttachCommand { - /// Create a new attach command - #[must_use] - pub fn new(service: impl Into) -> Self { - Self { - service: service.into(), - sig_proxy: true, // Default to true - ..Default::default() - } - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Set detach keys - #[must_use] - pub fn detach_keys(mut self, keys: impl Into) -> Self { - self.detach_keys = Some(keys.into()); - self - } - - /// Set container index - #[must_use] - pub fn index(mut self, index: u32) -> Self { - self.index = Some(index); - self - } - - /// Don't attach to STDIN - #[must_use] - pub fn no_stdin(mut self) -> Self { - self.no_stdin = true; - self - } - - /// Disable signal proxy - #[must_use] - pub fn no_sig_proxy(mut self) -> Self { - self.sig_proxy = false; - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["attach".to_string()]; - - // Add flags - if let Some(ref keys) = self.detach_keys { - args.push("--detach-keys".to_string()); - args.push(keys.clone()); - } - - if let Some(index) = self.index { - args.push("--index".to_string()); - args.push(index.to_string()); - } - - if self.no_stdin { - args.push("--no-stdin".to_string()); - } - - if !self.sig_proxy { - args.push("--sig-proxy=false".to_string()); - } - - // Add service - args.push(self.service.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeAttachCommand { - type Output = AttachResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(AttachResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_attach_command_basic() { - let cmd = ComposeAttachCommand::new("web"); - let args = cmd.build_args(); - assert_eq!(args[0], "attach"); - assert!(args.contains(&"web".to_string())); - } - - #[test] - fn test_attach_command_with_detach_keys() { - let cmd = ComposeAttachCommand::new("web").detach_keys("ctrl-p,ctrl-q"); - let args = cmd.build_args(); - assert!(args.contains(&"--detach-keys".to_string())); - assert!(args.contains(&"ctrl-p,ctrl-q".to_string())); - } - - #[test] - fn test_attach_command_with_index() { - let cmd = ComposeAttachCommand::new("web").index(2).no_stdin(); - let args = cmd.build_args(); - assert!(args.contains(&"--index".to_string())); - assert!(args.contains(&"2".to_string())); - assert!(args.contains(&"--no-stdin".to_string())); - } - - #[test] - fn test_attach_command_with_no_sig_proxy() { - let cmd = ComposeAttachCommand::new("worker").no_sig_proxy(); - let args = cmd.build_args(); - assert!(args.contains(&"--sig-proxy=false".to_string())); - } -} diff --git a/src/compose/build.rs b/src/compose/build.rs deleted file mode 100644 index 56f3aa54..00000000 --- a/src/compose/build.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Docker Compose build command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose build command builder -#[derive(Debug, Clone, Default)] -#[allow(dead_code)] // Stub implementation -#[allow(clippy::struct_excessive_bools)] // Will be refactored when implemented -pub struct ComposeBuildCommand { - config: ComposeConfig, - services: Vec, - no_cache: bool, - pull: bool, - quiet: bool, - build_arg: Vec<(String, String)>, - parallel: bool, -} - -impl ComposeBuildCommand { - /// Create a new compose build command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Execute the build command - /// - /// # Errors - /// - /// Returns an error if the docker compose build command fails - pub async fn run(&self) -> Result { - self.execute().await - } -} - -#[async_trait] -impl ComposeCommand for ComposeBuildCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "build" - } - - fn build_args(&self) -> Vec { - Vec::new() - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_build_basic() { - let cmd = ComposeBuildCommand::new(); - assert_eq!(cmd.subcommand(), "build"); - assert_eq!(cmd.build_args(), Vec::::new()); - } - - #[test] - fn test_compose_build_with_config() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .project_name("test-project"); - - let cmd = ComposeBuildCommand { - config: config.clone(), - ..Default::default() - }; - - assert_eq!(cmd.config().project_name, Some("test-project".to_string())); - } - - #[test] - fn test_compose_build_future_implementation() { - // Test that fields exist for future implementation - let cmd = ComposeBuildCommand { - config: ComposeConfig::new(), - services: vec!["web".to_string(), "db".to_string()], - no_cache: true, - pull: true, - quiet: false, - build_arg: vec![("VERSION".to_string(), "1.0".to_string())], - parallel: true, - }; - - // Currently returns empty args as it's a stub - assert_eq!(cmd.build_args(), Vec::::new()); - - // When fully implemented, it should build proper args - // Future test would verify: - // assert!(args.contains(&"--no-cache".to_string())); - // assert!(args.contains(&"--pull".to_string())); - // assert!(args.contains(&"web".to_string())); - // assert!(args.contains(&"db".to_string())); - } -} diff --git a/src/compose/config.rs b/src/compose/config.rs deleted file mode 100644 index 6ff01757..00000000 --- a/src/compose/config.rs +++ /dev/null @@ -1,288 +0,0 @@ -//! Docker Compose config command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose config command -/// -/// Validates and displays the Compose configuration. -#[derive(Debug, Clone, Default)] -#[allow(clippy::struct_excessive_bools)] -pub struct ComposeConfigCommand { - /// Base configuration - pub config: ComposeConfig, - /// Format output - pub format: Option, - /// Resolve image digests - pub resolve_image_digests: bool, - /// Don't interpolate environment - pub no_interpolate: bool, - /// Don't normalize paths - pub no_normalize: bool, - /// Don't check consistency - pub no_consistency: bool, - /// Show services - pub services: bool, - /// Show volumes - pub volumes: bool, - /// Show profiles - pub profiles: bool, - /// Show images - pub images: bool, - /// Hash of services to include - pub hash: Option, - /// Output file - pub output: Option, - /// Quiet mode - pub quiet: bool, -} - -/// Config output format -#[derive(Debug, Clone, Copy)] -pub enum ConfigFormat { - /// YAML format (default) - Yaml, - /// JSON format - Json, -} - -impl ConfigFormat { - /// Convert to command line argument - #[must_use] - pub fn as_arg(&self) -> &str { - match self { - Self::Yaml => "yaml", - Self::Json => "json", - } - } -} - -/// Result from config command -#[derive(Debug, Clone)] -pub struct ConfigResult { - /// The configuration output (YAML or JSON) - pub config: String, - /// Whether the config is valid - pub is_valid: bool, -} - -impl ComposeConfigCommand { - /// Create a new config command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Set output format - #[must_use] - pub fn format(mut self, format: ConfigFormat) -> Self { - self.format = Some(format); - self - } - - /// Resolve image digests - #[must_use] - pub fn resolve_image_digests(mut self) -> Self { - self.resolve_image_digests = true; - self - } - - /// Don't interpolate environment - #[must_use] - pub fn no_interpolate(mut self) -> Self { - self.no_interpolate = true; - self - } - - /// Don't normalize paths - #[must_use] - pub fn no_normalize(mut self) -> Self { - self.no_normalize = true; - self - } - - /// Don't check consistency - #[must_use] - pub fn no_consistency(mut self) -> Self { - self.no_consistency = true; - self - } - - /// Show services only - #[must_use] - pub fn services(mut self) -> Self { - self.services = true; - self - } - - /// Show volumes only - #[must_use] - pub fn volumes(mut self) -> Self { - self.volumes = true; - self - } - - /// Show profiles only - #[must_use] - pub fn profiles(mut self) -> Self { - self.profiles = true; - self - } - - /// Show images only - #[must_use] - pub fn images(mut self) -> Self { - self.images = true; - self - } - - /// Set services hash - #[must_use] - pub fn hash(mut self, hash: impl Into) -> Self { - self.hash = Some(hash.into()); - self - } - - /// Set output file - #[must_use] - pub fn output(mut self, path: impl Into) -> Self { - self.output = Some(path.into()); - self - } - - /// Enable quiet mode - #[must_use] - pub fn quiet(mut self) -> Self { - self.quiet = true; - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["config".to_string()]; - - // Add format - if let Some(format) = &self.format { - args.push("--format".to_string()); - args.push(format.as_arg().to_string()); - } - - // Add flags - if self.resolve_image_digests { - args.push("--resolve-image-digests".to_string()); - } - if self.no_interpolate { - args.push("--no-interpolate".to_string()); - } - if self.no_normalize { - args.push("--no-normalize".to_string()); - } - if self.no_consistency { - args.push("--no-consistency".to_string()); - } - if self.services { - args.push("--services".to_string()); - } - if self.volumes { - args.push("--volumes".to_string()); - } - if self.profiles { - args.push("--profiles".to_string()); - } - if self.images { - args.push("--images".to_string()); - } - if self.quiet { - args.push("--quiet".to_string()); - } - - // Add hash - if let Some(hash) = &self.hash { - args.push("--hash".to_string()); - args.push(hash.clone()); - } - - // Add output - if let Some(output) = &self.output { - args.push("--output".to_string()); - args.push(output.clone()); - } - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeConfigCommand { - type Output = ConfigResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(ConfigResult { - config: output.stdout, - is_valid: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config_command_basic() { - let cmd = ComposeConfigCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "config"); - } - - #[test] - fn test_config_command_with_format() { - let cmd = ComposeConfigCommand::new().format(ConfigFormat::Json); - let args = cmd.build_args(); - assert!(args.contains(&"--format".to_string())); - assert!(args.contains(&"json".to_string())); - } - - #[test] - fn test_config_command_with_flags() { - let cmd = ComposeConfigCommand::new() - .resolve_image_digests() - .no_interpolate() - .services() - .quiet(); - let args = cmd.build_args(); - assert!(args.contains(&"--resolve-image-digests".to_string())); - assert!(args.contains(&"--no-interpolate".to_string())); - assert!(args.contains(&"--services".to_string())); - assert!(args.contains(&"--quiet".to_string())); - } -} diff --git a/src/compose/convert.rs b/src/compose/convert.rs deleted file mode 100644 index 5fd3821a..00000000 --- a/src/compose/convert.rs +++ /dev/null @@ -1,327 +0,0 @@ -//! Docker Compose convert command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose convert command -/// -/// Convert compose files to different formats or validate them. -#[derive(Debug, Clone, Default)] -#[allow(clippy::struct_excessive_bools)] -pub struct ComposeConvertCommand { - /// Base configuration - pub config: ComposeConfig, - /// Output format - pub format: Option, - /// Resolve image digests - pub resolve_image_digests: bool, - /// Don't interpolate environment - pub no_interpolate: bool, - /// Don't normalize paths - pub no_normalize: bool, - /// Don't check consistency - pub no_consistency: bool, - /// Show services - pub services: bool, - /// Show volumes - pub volumes: bool, - /// Show profiles - pub profiles: bool, - /// Show images - pub images: bool, - /// Hash of services to include - pub hash: Option, - /// Output file - pub output: Option, - /// Quiet mode - pub quiet: bool, -} - -/// Convert output format -#[derive(Debug, Clone, Copy)] -pub enum ConvertFormat { - /// YAML format (default) - Yaml, - /// JSON format - Json, -} - -impl ConvertFormat { - /// Convert to command line argument - #[must_use] - pub fn as_arg(&self) -> &str { - match self { - Self::Yaml => "yaml", - Self::Json => "json", - } - } -} - -/// Result from convert command -#[derive(Debug, Clone)] -pub struct ConvertResult { - /// The converted configuration (YAML or JSON) - pub config: String, - /// Whether the conversion succeeded - pub success: bool, -} - -impl ComposeConvertCommand { - /// Create a new convert command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Set output format - #[must_use] - pub fn format(mut self, format: ConvertFormat) -> Self { - self.format = Some(format); - self - } - - /// Resolve image digests - #[must_use] - pub fn resolve_image_digests(mut self) -> Self { - self.resolve_image_digests = true; - self - } - - /// Don't interpolate environment - #[must_use] - pub fn no_interpolate(mut self) -> Self { - self.no_interpolate = true; - self - } - - /// Don't normalize paths - #[must_use] - pub fn no_normalize(mut self) -> Self { - self.no_normalize = true; - self - } - - /// Don't check consistency - #[must_use] - pub fn no_consistency(mut self) -> Self { - self.no_consistency = true; - self - } - - /// Show services only - #[must_use] - pub fn services(mut self) -> Self { - self.services = true; - self - } - - /// Show volumes only - #[must_use] - pub fn volumes(mut self) -> Self { - self.volumes = true; - self - } - - /// Show profiles only - #[must_use] - pub fn profiles(mut self) -> Self { - self.profiles = true; - self - } - - /// Show images only - #[must_use] - pub fn images(mut self) -> Self { - self.images = true; - self - } - - /// Set services hash - #[must_use] - pub fn hash(mut self, hash: impl Into) -> Self { - self.hash = Some(hash.into()); - self - } - - /// Set output file - #[must_use] - pub fn output(mut self, path: impl Into) -> Self { - self.output = Some(path.into()); - self - } - - /// Enable quiet mode - #[must_use] - pub fn quiet(mut self) -> Self { - self.quiet = true; - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["convert".to_string()]; - - // Add format - if let Some(format) = &self.format { - args.push("--format".to_string()); - args.push(format.as_arg().to_string()); - } - - // Add flags - if self.resolve_image_digests { - args.push("--resolve-image-digests".to_string()); - } - if self.no_interpolate { - args.push("--no-interpolate".to_string()); - } - if self.no_normalize { - args.push("--no-normalize".to_string()); - } - if self.no_consistency { - args.push("--no-consistency".to_string()); - } - if self.services { - args.push("--services".to_string()); - } - if self.volumes { - args.push("--volumes".to_string()); - } - if self.profiles { - args.push("--profiles".to_string()); - } - if self.images { - args.push("--images".to_string()); - } - if self.quiet { - args.push("--quiet".to_string()); - } - - // Add hash - if let Some(hash) = &self.hash { - args.push("--hash".to_string()); - args.push(hash.clone()); - } - - // Add output - if let Some(output) = &self.output { - args.push("--output".to_string()); - args.push(output.clone()); - } - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeConvertCommand { - type Output = ConvertResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(ConvertResult { - config: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -impl ConvertResult { - /// Check if the output is valid JSON - #[must_use] - pub fn is_json(&self) -> bool { - serde_json::from_str::(&self.config).is_ok() - } - - /// Check if the output is likely YAML - #[must_use] - pub fn is_yaml(&self) -> bool { - !self.config.is_empty() && (self.config.contains(':') || self.config.contains('-')) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_convert_command_basic() { - let cmd = ComposeConvertCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "convert"); - } - - #[test] - fn test_convert_command_with_format() { - let cmd = ComposeConvertCommand::new().format(ConvertFormat::Json); - let args = cmd.build_args(); - assert!(args.contains(&"--format".to_string())); - assert!(args.contains(&"json".to_string())); - } - - #[test] - fn test_convert_command_with_flags() { - let cmd = ComposeConvertCommand::new() - .resolve_image_digests() - .no_interpolate() - .services() - .quiet(); - let args = cmd.build_args(); - assert!(args.contains(&"--resolve-image-digests".to_string())); - assert!(args.contains(&"--no-interpolate".to_string())); - assert!(args.contains(&"--services".to_string())); - assert!(args.contains(&"--quiet".to_string())); - } - - #[test] - fn test_convert_command_with_output() { - let cmd = ComposeConvertCommand::new() - .output("docker-compose.json") - .format(ConvertFormat::Json); - let args = cmd.build_args(); - assert!(args.contains(&"--output".to_string())); - assert!(args.contains(&"docker-compose.json".to_string())); - } - - #[test] - fn test_convert_result_helpers() { - let json_result = ConvertResult { - config: r#"{"version": "3.8"}"#.to_string(), - success: true, - }; - assert!(json_result.is_json()); - - let yaml_result = ConvertResult { - config: "version: '3.8'\nservices:\n web:\n image: nginx".to_string(), - success: true, - }; - assert!(yaml_result.is_yaml()); - } -} diff --git a/src/compose/cp.rs b/src/compose/cp.rs deleted file mode 100644 index ff6af0b6..00000000 --- a/src/compose/cp.rs +++ /dev/null @@ -1,202 +0,0 @@ -//! Docker Compose cp command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; -use std::path::PathBuf; - -/// Docker Compose cp command -/// -/// Copy files/folders between a service container and the local filesystem. -#[derive(Debug, Clone)] -pub struct ComposeCpCommand { - /// Base configuration - pub config: ComposeConfig, - /// Source path (can be container:path or local path) - pub source: String, - /// Destination path (can be container:path or local path) - pub destination: String, - /// Archive mode (preserve permissions) - pub archive: bool, - /// Follow symbolic links - pub follow_link: bool, - /// Index of the container (if service has multiple instances) - pub index: Option, -} - -/// Result from cp command -#[derive(Debug, Clone)] -pub struct CpResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeCpCommand { - /// Create a new cp command - #[must_use] - pub fn new(source: impl Into, destination: impl Into) -> Self { - Self { - config: ComposeConfig::default(), - source: source.into(), - destination: destination.into(), - archive: false, - follow_link: false, - index: None, - } - } - - /// Copy from container to local - #[must_use] - pub fn from_container( - service: impl Into, - container_path: impl Into, - local_path: impl Into, - ) -> Self { - let source = format!("{}:{}", service.into(), container_path.into()); - let destination = local_path.into().display().to_string(); - Self::new(source, destination) - } - - /// Copy from local to container - #[must_use] - pub fn to_container( - local_path: impl Into, - service: impl Into, - container_path: impl Into, - ) -> Self { - let source = local_path.into().display().to_string(); - let destination = format!("{}:{}", service.into(), container_path.into()); - Self::new(source, destination) - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Enable archive mode - #[must_use] - pub fn archive(mut self) -> Self { - self.archive = true; - self - } - - /// Follow symbolic links - #[must_use] - pub fn follow_link(mut self) -> Self { - self.follow_link = true; - self - } - - /// Set container index - #[must_use] - pub fn index(mut self, index: u32) -> Self { - self.index = Some(index); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["cp".to_string()]; - - // Add flags - if self.archive { - args.push("--archive".to_string()); - } - if self.follow_link { - args.push("--follow-link".to_string()); - } - - // Add index if specified - if let Some(index) = self.index { - args.push("--index".to_string()); - args.push(index.to_string()); - } - - // Add source and destination - args.push(self.source.clone()); - args.push(self.destination.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeCpCommand { - type Output = CpResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(CpResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_cp_command_basic() { - let cmd = ComposeCpCommand::new("web:/app/config.yml", "./config.yml"); - let args = cmd.build_args(); - assert_eq!(args[0], "cp"); - assert!(args.contains(&"web:/app/config.yml".to_string())); - assert!(args.contains(&"./config.yml".to_string())); - } - - #[test] - fn test_cp_from_container() { - let cmd = ComposeCpCommand::from_container("web", "/app/logs", "./logs"); - let args = cmd.build_args(); - assert!(args.contains(&"web:/app/logs".to_string())); - assert!(args.contains(&"./logs".to_string())); - } - - #[test] - fn test_cp_to_container() { - let cmd = ComposeCpCommand::to_container("./config.yml", "web", "/app/config.yml"); - let args = cmd.build_args(); - assert!(args.contains(&"./config.yml".to_string())); - assert!(args.contains(&"web:/app/config.yml".to_string())); - } - - #[test] - fn test_cp_command_with_flags() { - let cmd = ComposeCpCommand::new("web:/data", "./data") - .archive() - .follow_link() - .index(1); - let args = cmd.build_args(); - assert!(args.contains(&"--archive".to_string())); - assert!(args.contains(&"--follow-link".to_string())); - assert!(args.contains(&"--index".to_string())); - assert!(args.contains(&"1".to_string())); - } -} diff --git a/src/compose/create.rs b/src/compose/create.rs deleted file mode 100644 index e98e51c5..00000000 --- a/src/compose/create.rs +++ /dev/null @@ -1,248 +0,0 @@ -//! Docker Compose create command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose create command -/// -/// Create services without starting them. -#[derive(Debug, Clone, Default)] -#[allow(clippy::struct_excessive_bools)] -pub struct ComposeCreateCommand { - /// Base configuration - pub config: ComposeConfig, - /// Build images before creating containers - pub build: bool, - /// Don't build images, even if missing - pub no_build: bool, - /// Force recreate containers - pub force_recreate: bool, - /// Don't recreate containers if they exist - pub no_recreate: bool, - /// Pull images before creating - pub pull: Option, - /// Remove orphaned containers - pub remove_orphans: bool, - /// Services to create - pub services: Vec, -} - -/// Pull policy for images -#[derive(Debug, Clone, Copy)] -pub enum PullPolicy { - /// Always pull images - Always, - /// Never pull images - Never, - /// Pull missing images (default) - Missing, - /// Pull images if local is older - Build, -} - -impl PullPolicy { - /// Convert to command line argument - #[must_use] - pub fn as_arg(&self) -> &str { - match self { - Self::Always => "always", - Self::Never => "never", - Self::Missing => "missing", - Self::Build => "build", - } - } -} - -/// Result from create command -#[derive(Debug, Clone)] -pub struct CreateResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeCreateCommand { - /// Create a new create command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Build images before creating - #[must_use] - pub fn build(mut self) -> Self { - self.build = true; - self - } - - /// Don't build images - #[must_use] - pub fn no_build(mut self) -> Self { - self.no_build = true; - self - } - - /// Force recreate containers - #[must_use] - pub fn force_recreate(mut self) -> Self { - self.force_recreate = true; - self - } - - /// Don't recreate containers - #[must_use] - pub fn no_recreate(mut self) -> Self { - self.no_recreate = true; - self - } - - /// Set pull policy - #[must_use] - pub fn pull(mut self, policy: PullPolicy) -> Self { - self.pull = Some(policy); - self - } - - /// Remove orphaned containers - #[must_use] - pub fn remove_orphans(mut self) -> Self { - self.remove_orphans = true; - self - } - - /// Add a service to create - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to create - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["create".to_string()]; - - // Add flags - if self.build { - args.push("--build".to_string()); - } - if self.no_build { - args.push("--no-build".to_string()); - } - if self.force_recreate { - args.push("--force-recreate".to_string()); - } - if self.no_recreate { - args.push("--no-recreate".to_string()); - } - if self.remove_orphans { - args.push("--remove-orphans".to_string()); - } - - // Add pull policy - if let Some(pull) = &self.pull { - args.push("--pull".to_string()); - args.push(pull.as_arg().to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeCreateCommand { - type Output = CreateResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(CreateResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_create_command_basic() { - let cmd = ComposeCreateCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "create"); - } - - #[test] - fn test_create_command_with_build() { - let cmd = ComposeCreateCommand::new().build().force_recreate(); - let args = cmd.build_args(); - assert!(args.contains(&"--build".to_string())); - assert!(args.contains(&"--force-recreate".to_string())); - } - - #[test] - fn test_create_command_with_pull() { - let cmd = ComposeCreateCommand::new() - .pull(PullPolicy::Always) - .no_recreate(); - let args = cmd.build_args(); - assert!(args.contains(&"--pull".to_string())); - assert!(args.contains(&"always".to_string())); - assert!(args.contains(&"--no-recreate".to_string())); - } - - #[test] - fn test_create_command_with_services() { - let cmd = ComposeCreateCommand::new() - .service("web") - .service("db") - .remove_orphans(); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"db".to_string())); - assert!(args.contains(&"--remove-orphans".to_string())); - } -} diff --git a/src/compose/down.rs b/src/compose/down.rs deleted file mode 100644 index 8644ebdf..00000000 --- a/src/compose/down.rs +++ /dev/null @@ -1,281 +0,0 @@ -//! Docker Compose down command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; -use std::time::Duration; - -/// Docker Compose down command builder -#[derive(Debug, Clone)] -pub struct ComposeDownCommand { - /// Base compose configuration - config: ComposeConfig, - /// Remove images - remove_images: Option, - /// Remove named volumes - volumes: bool, - /// Remove orphan containers - remove_orphans: bool, - /// Timeout for container shutdown - timeout: Option, - /// Services to stop (empty for all) - services: Vec, -} - -/// Image removal options for compose down -#[derive(Debug, Clone, Copy)] -pub enum RemoveImages { - /// Remove all images used by services - All, - /// Remove only images that don't have a custom tag - Local, -} - -impl std::fmt::Display for RemoveImages { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::All => write!(f, "all"), - Self::Local => write!(f, "local"), - } - } -} - -impl ComposeDownCommand { - /// Create a new compose down command - #[must_use] - pub fn new() -> Self { - Self { - config: ComposeConfig::new(), - remove_images: None, - volumes: false, - remove_orphans: false, - timeout: None, - services: Vec::new(), - } - } - - /// Create with a specific compose configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Self::new() - } - } - - /// Remove images (all or local) - #[must_use] - pub fn remove_images(mut self, policy: RemoveImages) -> Self { - self.remove_images = Some(policy); - self - } - - /// Remove named volumes declared in the volumes section - #[must_use] - pub fn volumes(mut self) -> Self { - self.volumes = true; - self - } - - /// Remove containers for services not defined in the compose file - #[must_use] - pub fn remove_orphans(mut self) -> Self { - self.remove_orphans = true; - self - } - - /// Set timeout for container shutdown - #[must_use] - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = Some(timeout); - self - } - - /// Add a service to stop - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - /// Set compose file - #[must_use] - pub fn file(mut self, path: impl Into) -> Self { - self.config = self.config.file(path); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config = self.config.project_name(name); - self - } - - /// Execute the compose down command - /// - /// # Errors - /// Returns an error if: - /// - Docker Compose is not installed - /// - Compose file is not found - /// - Container stop/removal fails - pub async fn run(&self) -> Result { - let output = self.execute().await?; - - Ok(ComposeDownResult { - output, - removed_volumes: self.volumes, - removed_images: self.remove_images.is_some(), - }) - } -} - -impl Default for ComposeDownCommand { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl ComposeCommand for ComposeDownCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "down" - } - - fn build_args(&self) -> Vec { - let mut args = Vec::new(); - - if let Some(ref remove) = self.remove_images { - args.push("--rmi".to_string()); - args.push(remove.to_string()); - } - - if self.volumes { - args.push("--volumes".to_string()); - } - - if self.remove_orphans { - args.push("--remove-orphans".to_string()); - } - - if let Some(timeout) = self.timeout { - args.push("--timeout".to_string()); - args.push(timeout.as_secs().to_string()); - } - - // Add service names at the end - args.extend(self.services.clone()); - - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -/// Result from compose down command -#[derive(Debug, Clone)] -pub struct ComposeDownResult { - /// Raw command output - pub output: ComposeOutput, - /// Whether volumes were removed - pub removed_volumes: bool, - /// Whether images were removed - pub removed_images: bool, -} - -impl ComposeDownResult { - /// Check if the command was successful - #[must_use] - pub fn success(&self) -> bool { - self.output.success - } - - /// Check if volumes were removed - #[must_use] - pub fn volumes_removed(&self) -> bool { - self.removed_volumes - } - - /// Check if images were removed - #[must_use] - pub fn images_removed(&self) -> bool { - self.removed_images - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_down_basic() { - let cmd = ComposeDownCommand::new(); - let args = cmd.build_args(); - assert!(args.is_empty()); - } - - #[test] - fn test_compose_down_with_volumes() { - let cmd = ComposeDownCommand::new().volumes(); - let args = cmd.build_args(); - assert_eq!(args, vec!["--volumes"]); - } - - #[test] - fn test_compose_down_remove_images() { - let cmd = ComposeDownCommand::new().remove_images(RemoveImages::All); - let args = cmd.build_args(); - assert_eq!(args, vec!["--rmi", "all"]); - } - - #[test] - fn test_compose_down_all_options() { - let cmd = ComposeDownCommand::new() - .remove_images(RemoveImages::Local) - .volumes() - .remove_orphans() - .timeout(Duration::from_secs(30)) - .service("web") - .service("db"); - - let args = cmd.build_args(); - assert_eq!( - args, - vec![ - "--rmi", - "local", - "--volumes", - "--remove-orphans", - "--timeout", - "30", - "web", - "db" - ] - ); - } - - #[test] - fn test_remove_images_display() { - assert_eq!(RemoveImages::All.to_string(), "all"); - assert_eq!(RemoveImages::Local.to_string(), "local"); - } -} diff --git a/src/compose/events.rs b/src/compose/events.rs deleted file mode 100644 index ff07f582..00000000 --- a/src/compose/events.rs +++ /dev/null @@ -1,212 +0,0 @@ -//! Docker Compose events command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; -use serde::Deserialize; - -/// Docker Compose events command -/// -/// Stream container events for services. -#[derive(Debug, Clone, Default)] -pub struct ComposeEventsCommand { - /// Base configuration - pub config: ComposeConfig, - /// Output format as JSON - pub json: bool, - /// Start timestamp - pub since: Option, - /// End timestamp - pub until: Option, - /// Services to get events for - pub services: Vec, -} - -/// Event from Docker Compose -#[derive(Debug, Clone, Deserialize)] -pub struct ComposeEvent { - /// Time of the event - pub time: String, - /// Type of the event - #[serde(rename = "type")] - pub event_type: String, - /// Action that occurred - pub action: String, - /// Service name - pub service: Option, - /// Container ID - pub container: Option, - /// Additional attributes - pub attributes: Option, -} - -/// Result from events command -#[derive(Debug, Clone)] -pub struct EventsResult { - /// Raw output from the command - pub output: String, - /// Parsed events (if JSON format) - pub events: Vec, -} - -impl ComposeEventsCommand { - /// Create a new events command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Output as JSON - #[must_use] - pub fn json(mut self) -> Self { - self.json = true; - self - } - - /// Set start timestamp - #[must_use] - pub fn since(mut self, timestamp: impl Into) -> Self { - self.since = Some(timestamp.into()); - self - } - - /// Set end timestamp - #[must_use] - pub fn until(mut self, timestamp: impl Into) -> Self { - self.until = Some(timestamp.into()); - self - } - - /// Add a service to get events for - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["events".to_string()]; - - // Add flags - if self.json { - args.push("--json".to_string()); - } - - // Add timestamps - if let Some(since) = &self.since { - args.push("--since".to_string()); - args.push(since.clone()); - } - if let Some(until) = &self.until { - args.push("--until".to_string()); - args.push(until.clone()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeEventsCommand { - type Output = EventsResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - // Parse events if JSON format - let events = if self.json { - output - .stdout - .lines() - .filter_map(|line| serde_json::from_str(line).ok()) - .collect() - } else { - Vec::new() - }; - - Ok(EventsResult { - output: output.stdout, - events, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_events_command_basic() { - let cmd = ComposeEventsCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "events"); - } - - #[test] - fn test_events_command_with_json() { - let cmd = ComposeEventsCommand::new().json(); - let args = cmd.build_args(); - assert!(args.contains(&"--json".to_string())); - } - - #[test] - fn test_events_command_with_timestamps() { - let cmd = ComposeEventsCommand::new() - .since("2025-08-23T00:00:00") - .until("2025-08-23T23:59:59"); - let args = cmd.build_args(); - assert!(args.contains(&"--since".to_string())); - assert!(args.contains(&"2025-08-23T00:00:00".to_string())); - assert!(args.contains(&"--until".to_string())); - assert!(args.contains(&"2025-08-23T23:59:59".to_string())); - } - - #[test] - fn test_events_command_with_services() { - let cmd = ComposeEventsCommand::new().service("web").service("db"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"db".to_string())); - } -} diff --git a/src/compose/exec.rs b/src/compose/exec.rs deleted file mode 100644 index b2b8b271..00000000 --- a/src/compose/exec.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Docker Compose exec command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose exec command builder -#[derive(Debug, Clone, Default)] -#[allow(dead_code)] // Stub implementation -pub struct ComposeExecCommand { - config: ComposeConfig, - service: String, - command: Vec, - detach: bool, - user: Option, - workdir: Option, -} - -impl ComposeExecCommand { - /// Create a new compose exec command - #[must_use] - pub fn new(service: impl Into) -> Self { - Self { - service: service.into(), - ..Self::default() - } - } - - /// Execute the exec command - /// - /// # Errors - /// - /// Returns an error if the docker compose exec command fails - pub async fn run(&self) -> Result { - self.execute().await - } -} - -#[async_trait] -impl ComposeCommand for ComposeExecCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "exec" - } - - fn build_args(&self) -> Vec { - let mut args = vec![self.service.clone()]; - args.extend(self.command.clone()); - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_exec_basic() { - let cmd = ComposeExecCommand::new("web"); - assert_eq!(cmd.service, "web"); - assert_eq!(cmd.subcommand(), "exec"); - let args = cmd.build_args(); - assert_eq!(args, vec!["web"]); - } - - #[test] - fn test_compose_exec_with_command() { - let mut cmd = ComposeExecCommand::new("db"); - cmd.command = vec!["psql".to_string(), "-U".to_string(), "postgres".to_string()]; - - let args = cmd.build_args(); - assert_eq!(args, vec!["db", "psql", "-U", "postgres"]); - } - - #[test] - fn test_compose_exec_with_config() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .project_name("myapp"); - - let mut cmd = ComposeExecCommand::new("web"); - cmd.config = config; - cmd.command = vec!["bash".to_string()]; - - assert_eq!(cmd.config().project_name, Some("myapp".to_string())); - assert_eq!(cmd.build_args(), vec!["web", "bash"]); - } - - #[test] - fn test_compose_exec_future_options() { - // Test that fields exist for future implementation - let cmd = ComposeExecCommand { - config: ComposeConfig::new(), - service: "app".to_string(), - command: vec!["sh".to_string()], - detach: true, - user: Some("root".to_string()), - workdir: Some("/app".to_string()), - }; - - // Currently only includes service and command - let args = cmd.build_args(); - assert_eq!(args, vec!["app", "sh"]); - - // When fully implemented, it should include options: - // assert!(args.contains(&"--detach".to_string())); - // assert!(args.contains(&"--user".to_string())); - // assert!(args.contains(&"root".to_string())); - // assert!(args.contains(&"--workdir".to_string())); - // assert!(args.contains(&"/app".to_string())); - } -} diff --git a/src/compose/images.rs b/src/compose/images.rs deleted file mode 100644 index f7d70e6b..00000000 --- a/src/compose/images.rs +++ /dev/null @@ -1,239 +0,0 @@ -//! Docker Compose images command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; -use serde::Deserialize; - -/// Docker Compose images command -/// -/// List images used by services. -#[derive(Debug, Clone, Default)] -pub struct ComposeImagesCommand { - /// Base configuration - pub config: ComposeConfig, - /// Format output (table, json) - pub format: Option, - /// Only display image IDs - pub quiet: bool, - /// Services to list images for - pub services: Vec, -} - -/// Images output format -#[derive(Debug, Clone, Copy)] -pub enum ImagesFormat { - /// Table format (default) - Table, - /// JSON format - Json, -} - -impl ImagesFormat { - /// Convert to command line argument - #[must_use] - pub fn as_arg(&self) -> &str { - match self { - Self::Table => "table", - Self::Json => "json", - } - } -} - -/// Image information -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct ImageInfo { - /// Container name - pub container: String, - /// Repository - pub repository: String, - /// Tag - pub tag: String, - /// Image ID - pub image_id: String, - /// Size - pub size: String, -} - -/// Result from images command -#[derive(Debug, Clone)] -pub struct ImagesResult { - /// List of images - pub images: Vec, - /// Raw output (for non-JSON formats) - pub raw_output: String, -} - -impl ComposeImagesCommand { - /// Create a new images command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Set output format - #[must_use] - pub fn format(mut self, format: ImagesFormat) -> Self { - self.format = Some(format); - self - } - - /// Set output format to JSON - #[must_use] - pub fn format_json(mut self) -> Self { - self.format = Some(ImagesFormat::Json); - self - } - - /// Only display image IDs - #[must_use] - pub fn quiet(mut self) -> Self { - self.quiet = true; - self - } - - /// Add a service to list images for - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["images".to_string()]; - - // Add flags - if self.quiet { - args.push("--quiet".to_string()); - } - - // Add format - if let Some(format) = &self.format { - args.push("--format".to_string()); - args.push(format.as_arg().to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeImagesCommand { - type Output = ImagesResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - // Parse JSON output if format is JSON - let images = if matches!(self.format, Some(ImagesFormat::Json)) { - serde_json::from_str(&output.stdout).unwrap_or_default() - } else { - Vec::new() - }; - - Ok(ImagesResult { - images, - raw_output: output.stdout, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -impl ImagesResult { - /// Get unique images - #[must_use] - pub fn unique_images(&self) -> Vec { - let mut images: Vec<_> = self - .images - .iter() - .map(|img| format!("{}:{}", img.repository, img.tag)) - .collect(); - images.sort(); - images.dedup(); - images - } - - /// Get total size - #[must_use] - pub fn total_size(&self) -> String { - // This would need proper size parsing - // For now just return placeholder - "N/A".to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_images_command_basic() { - let cmd = ComposeImagesCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "images"); - } - - #[test] - fn test_images_command_with_format() { - let cmd = ComposeImagesCommand::new().format_json(); - let args = cmd.build_args(); - assert!(args.contains(&"--format".to_string())); - assert!(args.contains(&"json".to_string())); - } - - #[test] - fn test_images_command_with_quiet() { - let cmd = ComposeImagesCommand::new().quiet(); - let args = cmd.build_args(); - assert!(args.contains(&"--quiet".to_string())); - } - - #[test] - fn test_images_command_with_services() { - let cmd = ComposeImagesCommand::new().service("web").service("db"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"db".to_string())); - } -} diff --git a/src/compose/kill.rs b/src/compose/kill.rs deleted file mode 100644 index 17e4af34..00000000 --- a/src/compose/kill.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! Docker Compose kill command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose kill command -/// -/// Force stop service containers. -#[derive(Debug, Clone, Default)] -pub struct ComposeKillCommand { - /// Base configuration - pub config: ComposeConfig, - /// Signal to send (default: SIGKILL) - pub signal: Option, - /// Remove containers after killing - pub remove_orphans: bool, - /// Services to kill - pub services: Vec, -} - -/// Result from kill command -#[derive(Debug, Clone)] -pub struct KillResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeKillCommand { - /// Create a new kill command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Set signal to send - #[must_use] - pub fn signal(mut self, signal: impl Into) -> Self { - self.signal = Some(signal.into()); - self - } - - /// Remove orphaned containers - #[must_use] - pub fn remove_orphans(mut self) -> Self { - self.remove_orphans = true; - self - } - - /// Add a service to kill - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to kill - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["kill".to_string()]; - - // Add signal - if let Some(signal) = &self.signal { - args.push("--signal".to_string()); - args.push(signal.clone()); - } - - // Add flags - if self.remove_orphans { - args.push("--remove-orphans".to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeKillCommand { - type Output = KillResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(KillResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_kill_command_basic() { - let cmd = ComposeKillCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "kill"); - } - - #[test] - fn test_kill_command_with_signal() { - let cmd = ComposeKillCommand::new().signal("SIGTERM"); - let args = cmd.build_args(); - assert!(args.contains(&"--signal".to_string())); - assert!(args.contains(&"SIGTERM".to_string())); - } - - #[test] - fn test_kill_command_with_services() { - let cmd = ComposeKillCommand::new() - .service("web") - .service("worker") - .remove_orphans(); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"worker".to_string())); - assert!(args.contains(&"--remove-orphans".to_string())); - } -} diff --git a/src/compose/logs.rs b/src/compose/logs.rs deleted file mode 100644 index b608ddb8..00000000 --- a/src/compose/logs.rs +++ /dev/null @@ -1,277 +0,0 @@ -//! Docker Compose logs command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose logs command builder -#[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for logs command -pub struct ComposeLogsCommand { - /// Base compose configuration - config: ComposeConfig, - /// Services to show logs for (empty for all) - services: Vec, - /// Follow log output - follow: bool, - /// Show timestamps - timestamps: bool, - /// Number of lines to show from the end - tail: Option, - /// Show logs since timestamp - since: Option, - /// Show logs until timestamp - until: Option, - /// Don't print prefix - no_log_prefix: bool, - /// Don't use colors - no_color: bool, -} - -impl ComposeLogsCommand { - /// Create a new compose logs command - #[must_use] - pub fn new() -> Self { - Self { - config: ComposeConfig::new(), - services: Vec::new(), - follow: false, - timestamps: false, - tail: None, - since: None, - until: None, - no_log_prefix: false, - no_color: false, - } - } - - /// Create with a specific compose configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Self::new() - } - } - - /// Add a service to show logs for - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Follow log output - #[must_use] - pub fn follow(mut self) -> Self { - self.follow = true; - self - } - - /// Show timestamps - #[must_use] - pub fn timestamps(mut self) -> Self { - self.timestamps = true; - self - } - - /// Number of lines to show from the end - #[must_use] - pub fn tail(mut self, lines: impl Into) -> Self { - self.tail = Some(lines.into()); - self - } - - /// Show logs since timestamp - #[must_use] - pub fn since(mut self, timestamp: impl Into) -> Self { - self.since = Some(timestamp.into()); - self - } - - /// Show logs until timestamp - #[must_use] - pub fn until(mut self, timestamp: impl Into) -> Self { - self.until = Some(timestamp.into()); - self - } - - /// Don't print prefix - #[must_use] - pub fn no_log_prefix(mut self) -> Self { - self.no_log_prefix = true; - self - } - - /// Don't use colors - #[must_use] - pub fn no_color(mut self) -> Self { - self.no_color = true; - self - } - - /// Set compose file - #[must_use] - pub fn file(mut self, path: impl Into) -> Self { - self.config = self.config.file(path); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config = self.config.project_name(name); - self - } - - /// Execute the compose logs command - /// - /// # Errors - /// Returns an error if: - /// - Docker Compose is not installed - /// - Compose file is not found - /// - Service doesn't exist - pub async fn run(&self) -> Result { - let output = self.execute().await?; - - Ok(ComposeLogsResult { - output, - services: self.services.clone(), - }) - } -} - -impl Default for ComposeLogsCommand { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl ComposeCommand for ComposeLogsCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "logs" - } - - fn build_args(&self) -> Vec { - let mut args = Vec::new(); - - if self.follow { - args.push("--follow".to_string()); - } - - if self.timestamps { - args.push("--timestamps".to_string()); - } - - if let Some(ref tail) = self.tail { - args.push("--tail".to_string()); - args.push(tail.clone()); - } - - if let Some(ref since) = self.since { - args.push("--since".to_string()); - args.push(since.clone()); - } - - if let Some(ref until) = self.until { - args.push("--until".to_string()); - args.push(until.clone()); - } - - if self.no_log_prefix { - args.push("--no-log-prefix".to_string()); - } - - if self.no_color { - args.push("--no-color".to_string()); - } - - // Add service names at the end - args.extend(self.services.clone()); - - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -/// Result from compose logs command -#[derive(Debug, Clone)] -pub struct ComposeLogsResult { - /// Raw command output - pub output: ComposeOutput, - /// Services logs were fetched for - pub services: Vec, -} - -impl ComposeLogsResult { - /// Check if the command was successful - #[must_use] - pub fn success(&self) -> bool { - self.output.success - } - - /// Get the services - #[must_use] - pub fn services(&self) -> &[String] { - &self.services - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_logs_basic() { - let cmd = ComposeLogsCommand::new(); - let args = cmd.build_args(); - assert!(args.is_empty()); - } - - #[test] - fn test_compose_logs_follow() { - let cmd = ComposeLogsCommand::new().follow().timestamps(); - let args = cmd.build_args(); - assert_eq!(args, vec!["--follow", "--timestamps"]); - } - - #[test] - fn test_compose_logs_with_tail() { - let cmd = ComposeLogsCommand::new().tail("100").service("web"); - let args = cmd.build_args(); - assert_eq!(args, vec!["--tail", "100", "web"]); - } - - #[test] - fn test_compose_logs_all_options() { - let cmd = ComposeLogsCommand::new() - .follow() - .timestamps() - .tail("50") - .since("2024-01-01T00:00:00") - .no_color() - .service("web") - .service("db"); - - let args = cmd.build_args(); - assert!(args.contains(&"--follow".to_string())); - assert!(args.contains(&"--timestamps".to_string())); - assert!(args.contains(&"--tail".to_string())); - assert!(args.contains(&"50".to_string())); - assert!(args.contains(&"--since".to_string())); - assert!(args.contains(&"2024-01-01T00:00:00".to_string())); - assert!(args.contains(&"--no-color".to_string())); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"db".to_string())); - } -} diff --git a/src/compose/ls.rs b/src/compose/ls.rs deleted file mode 100644 index 3e5cabb9..00000000 --- a/src/compose/ls.rs +++ /dev/null @@ -1,256 +0,0 @@ -//! Docker Compose ls command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; -use serde::Deserialize; - -/// Docker Compose ls command -/// -/// List running compose projects. -#[derive(Debug, Clone, Default)] -pub struct ComposeLsCommand { - /// Base configuration - pub config: ComposeConfig, - /// Show all projects (including stopped) - pub all: bool, - /// Filter by name - pub filter: Option, - /// Format output (table, json) - pub format: Option, - /// Only display project names - pub quiet: bool, -} - -/// Ls output format -#[derive(Debug, Clone, Copy)] -pub enum LsFormat { - /// Table format (default) - Table, - /// JSON format - Json, -} - -impl LsFormat { - /// Convert to command line argument - #[must_use] - pub fn as_arg(&self) -> &str { - match self { - Self::Table => "table", - Self::Json => "json", - } - } -} - -/// Compose project information -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct ComposeProject { - /// Project name - pub name: String, - /// Status - pub status: String, - /// Configuration files - #[serde(default)] - pub config_files: String, - /// Created timestamp - #[serde(default)] - pub created: String, -} - -/// Result from ls command -#[derive(Debug, Clone)] -pub struct LsResult { - /// List of compose projects - pub projects: Vec, - /// Raw output (for non-JSON formats) - pub raw_output: String, -} - -impl ComposeLsCommand { - /// Create a new ls command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Show all projects - #[must_use] - pub fn all(mut self) -> Self { - self.all = true; - self - } - - /// Filter projects - #[must_use] - pub fn filter(mut self, filter: impl Into) -> Self { - self.filter = Some(filter.into()); - self - } - - /// Set output format - #[must_use] - pub fn format(mut self, format: LsFormat) -> Self { - self.format = Some(format); - self - } - - /// Set output format to JSON - #[must_use] - pub fn format_json(mut self) -> Self { - self.format = Some(LsFormat::Json); - self - } - - /// Only display project names - #[must_use] - pub fn quiet(mut self) -> Self { - self.quiet = true; - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["ls".to_string()]; - - // Add flags - if self.all { - args.push("--all".to_string()); - } - if self.quiet { - args.push("--quiet".to_string()); - } - - // Add filter - if let Some(filter) = &self.filter { - args.push("--filter".to_string()); - args.push(filter.clone()); - } - - // Add format - if let Some(format) = &self.format { - args.push("--format".to_string()); - args.push(format.as_arg().to_string()); - } - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeLsCommand { - type Output = LsResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - // Parse JSON output if format is JSON - let projects = if matches!(self.format, Some(LsFormat::Json)) { - serde_json::from_str(&output.stdout).unwrap_or_default() - } else { - Vec::new() - }; - - Ok(LsResult { - projects, - raw_output: output.stdout, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -impl LsResult { - /// Get project names - #[must_use] - pub fn project_names(&self) -> Vec { - self.projects.iter().map(|p| p.name.clone()).collect() - } - - /// Check if a project exists - #[must_use] - pub fn has_project(&self, name: &str) -> bool { - self.projects.iter().any(|p| p.name == name) - } - - /// Get running projects - #[must_use] - pub fn running_projects(&self) -> Vec<&ComposeProject> { - self.projects - .iter() - .filter(|p| p.status.contains("running")) - .collect() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ls_command_basic() { - let cmd = ComposeLsCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "ls"); - } - - #[test] - fn test_ls_command_with_all() { - let cmd = ComposeLsCommand::new().all(); - let args = cmd.build_args(); - assert!(args.contains(&"--all".to_string())); - } - - #[test] - fn test_ls_command_with_format() { - let cmd = ComposeLsCommand::new().format_json(); - let args = cmd.build_args(); - assert!(args.contains(&"--format".to_string())); - assert!(args.contains(&"json".to_string())); - } - - #[test] - fn test_ls_command_with_filter() { - let cmd = ComposeLsCommand::new().filter("status=running").quiet(); - let args = cmd.build_args(); - assert!(args.contains(&"--filter".to_string())); - assert!(args.contains(&"status=running".to_string())); - assert!(args.contains(&"--quiet".to_string())); - } - - #[test] - fn test_ls_result_helpers() { - let result = LsResult { - projects: vec![ - ComposeProject { - name: "web".to_string(), - status: "running(3)".to_string(), - config_files: "docker-compose.yml".to_string(), - created: "2025-08-23".to_string(), - }, - ComposeProject { - name: "db".to_string(), - status: "exited(0)".to_string(), - config_files: "docker-compose.yml".to_string(), - created: "2025-08-23".to_string(), - }, - ], - raw_output: String::new(), - }; - - assert_eq!(result.project_names(), vec!["web", "db"]); - assert!(result.has_project("web")); - assert!(!result.has_project("cache")); - assert_eq!(result.running_projects().len(), 1); - } -} diff --git a/src/compose/pause.rs b/src/compose/pause.rs deleted file mode 100644 index d08a152a..00000000 --- a/src/compose/pause.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Docker Compose pause command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose pause command -/// -/// Pause services. -#[derive(Debug, Clone, Default)] -pub struct ComposePauseCommand { - /// Base configuration - pub config: ComposeConfig, - /// Services to pause - pub services: Vec, -} - -/// Result from pause command -#[derive(Debug, Clone)] -pub struct PauseResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposePauseCommand { - /// Create a new pause command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Add a service to pause - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to pause - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["pause".to_string()]; - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposePauseCommand { - type Output = PauseResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(PauseResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_pause_command_basic() { - let cmd = ComposePauseCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "pause"); - } - - #[test] - fn test_pause_command_with_services() { - let cmd = ComposePauseCommand::new().service("web").service("worker"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"worker".to_string())); - } -} diff --git a/src/compose/port.rs b/src/compose/port.rs deleted file mode 100644 index 2d3f8806..00000000 --- a/src/compose/port.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! Docker Compose port command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose port command -/// -/// Print the public port for a port binding. -#[derive(Debug, Clone, Default)] -pub struct ComposePortCommand { - /// Base configuration - pub config: ComposeConfig, - /// Service name - pub service: String, - /// Private port number - pub private_port: u16, - /// Protocol (tcp/udp) - pub protocol: Option, - /// Index of the container (if service has multiple instances) - pub index: Option, -} - -/// Result from port command -#[derive(Debug, Clone)] -pub struct PortResult { - /// The public port binding (host:port) - pub binding: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposePortCommand { - /// Create a new port command - #[must_use] - pub fn new(service: impl Into, private_port: u16) -> Self { - Self { - service: service.into(), - private_port, - ..Default::default() - } - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Set protocol (tcp/udp) - #[must_use] - pub fn protocol(mut self, protocol: impl Into) -> Self { - self.protocol = Some(protocol.into()); - self - } - - /// Set container index - #[must_use] - pub fn index(mut self, index: u32) -> Self { - self.index = Some(index); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["port".to_string()]; - - // Add index if specified - if let Some(index) = self.index { - args.push("--index".to_string()); - args.push(index.to_string()); - } - - // Add protocol if specified - if let Some(protocol) = &self.protocol { - args.push("--protocol".to_string()); - args.push(protocol.clone()); - } - - // Add service and port - args.push(self.service.clone()); - args.push(self.private_port.to_string()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposePortCommand { - type Output = PortResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(PortResult { - binding: output.stdout.trim().to_string(), - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -impl PortResult { - /// Parse the binding into host and port - #[must_use] - pub fn parse_binding(&self) -> Option<(String, u16)> { - let parts: Vec<&str> = self.binding.split(':').collect(); - if parts.len() == 2 { - if let Ok(port) = parts[1].parse::() { - return Some((parts[0].to_string(), port)); - } - } - None - } - - /// Get just the port number - #[must_use] - pub fn port(&self) -> Option { - self.parse_binding().map(|(_, port)| port) - } - - /// Get just the host - #[must_use] - pub fn host(&self) -> Option { - self.parse_binding().map(|(host, _)| host) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_port_command_basic() { - let cmd = ComposePortCommand::new("web", 80); - let args = cmd.build_args(); - assert_eq!(args[0], "port"); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"80".to_string())); - } - - #[test] - fn test_port_command_with_protocol() { - let cmd = ComposePortCommand::new("web", 53).protocol("udp"); - let args = cmd.build_args(); - assert!(args.contains(&"--protocol".to_string())); - assert!(args.contains(&"udp".to_string())); - } - - #[test] - fn test_port_command_with_index() { - let cmd = ComposePortCommand::new("web", 8080).index(2); - let args = cmd.build_args(); - assert!(args.contains(&"--index".to_string())); - assert!(args.contains(&"2".to_string())); - } - - #[test] - fn test_port_result_parsing() { - let result = PortResult { - binding: "0.0.0.0:32768".to_string(), - success: true, - }; - - assert_eq!(result.port(), Some(32768)); - assert_eq!(result.host(), Some("0.0.0.0".to_string())); - - let (host, port) = result.parse_binding().unwrap(); - assert_eq!(host, "0.0.0.0"); - assert_eq!(port, 32768); - } -} diff --git a/src/compose/ps.rs b/src/compose/ps.rs deleted file mode 100644 index 275f9996..00000000 --- a/src/compose/ps.rs +++ /dev/null @@ -1,361 +0,0 @@ -//! Docker Compose ps command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -/// Docker Compose ps command builder -#[derive(Debug, Clone)] -pub struct ComposePsCommand { - /// Base compose configuration - config: ComposeConfig, - /// Services to list (empty for all) - services: Vec, - /// Show all containers (including stopped) - all: bool, - /// Only display container IDs - quiet: bool, - /// Show services - show_services: bool, - /// Filter containers - filter: Vec, - /// Output format - format: Option, - /// Only show running containers - status: Option>, -} - -/// Container status filter -#[derive(Debug, Clone, Copy)] -pub enum ContainerStatus { - /// Paused containers - Paused, - /// Restarting containers - Restarting, - /// Running containers - Running, - /// Stopped containers - Stopped, -} - -impl std::fmt::Display for ContainerStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Paused => write!(f, "paused"), - Self::Restarting => write!(f, "restarting"), - Self::Running => write!(f, "running"), - Self::Stopped => write!(f, "stopped"), - } - } -} - -impl ComposePsCommand { - /// Create a new compose ps command - #[must_use] - pub fn new() -> Self { - Self { - config: ComposeConfig::new(), - services: Vec::new(), - all: false, - quiet: false, - show_services: false, - filter: Vec::new(), - format: None, - status: None, - } - } - - /// Create with a specific compose configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Self::new() - } - } - - /// Add a service to list - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Show all containers (default shows only running) - #[must_use] - pub fn all(mut self) -> Self { - self.all = true; - self - } - - /// Only display container IDs - #[must_use] - pub fn quiet(mut self) -> Self { - self.quiet = true; - self - } - - /// Display services - #[must_use] - pub fn services(mut self) -> Self { - self.show_services = true; - self - } - - /// Add a filter - #[must_use] - pub fn filter(mut self, filter: impl Into) -> Self { - self.filter.push(filter.into()); - self - } - - /// Set output format - #[must_use] - pub fn format(mut self, format: impl Into) -> Self { - self.format = Some(format.into()); - self - } - - /// Filter by status - #[must_use] - pub fn status(mut self, status: ContainerStatus) -> Self { - self.status.get_or_insert_with(Vec::new).push(status); - self - } - - /// Use JSON output format - #[must_use] - pub fn json(mut self) -> Self { - self.format = Some("json".to_string()); - self - } - - /// Set compose file - #[must_use] - pub fn file(mut self, path: impl Into) -> Self { - self.config = self.config.file(path); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config = self.config.project_name(name); - self - } - - /// Execute the compose ps command - /// - /// # Errors - /// Returns an error if: - /// - Docker Compose is not installed - /// - Compose file is not found - /// - Service lookup fails - pub async fn run(&self) -> Result { - let output = self.execute().await?; - - // Parse JSON output if format is json - let containers = if self.format.as_deref() == Some("json") { - Self::parse_json_output(&output.stdout) - } else { - Vec::new() - }; - - Ok(ComposePsResult { output, containers }) - } - - /// Parse JSON output into container info - fn parse_json_output(stdout: &str) -> Vec { - stdout - .lines() - .filter(|line| !line.trim().is_empty()) - .filter_map(|line| serde_json::from_str(line).ok()) - .collect() - } -} - -impl Default for ComposePsCommand { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl ComposeCommand for ComposePsCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "ps" - } - - fn build_args(&self) -> Vec { - let mut args = Vec::new(); - - if self.all { - args.push("--all".to_string()); - } - - if self.quiet { - args.push("--quiet".to_string()); - } - - if self.show_services { - args.push("--services".to_string()); - } - - for filter in &self.filter { - args.push("--filter".to_string()); - args.push(filter.clone()); - } - - if let Some(ref format) = self.format { - args.push("--format".to_string()); - args.push(format.clone()); - } - - if let Some(ref statuses) = self.status { - for status in statuses { - args.push("--status".to_string()); - args.push(status.to_string()); - } - } - - // Add service names at the end - args.extend(self.services.clone()); - - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -/// Container information from compose ps -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ComposeContainerInfo { - /// Container ID - #[serde(rename = "ID")] - pub id: String, - /// Container name - #[serde(rename = "Name")] - pub name: String, - /// Service name - #[serde(rename = "Service")] - pub service: String, - /// Container state - #[serde(rename = "State")] - pub state: String, - /// Health status - #[serde(rename = "Health")] - pub health: Option, - /// Exit code - #[serde(rename = "ExitCode")] - pub exit_code: Option, - /// Published ports - #[serde(rename = "Publishers")] - pub publishers: Option>, -} - -/// Port publishing information -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PortPublisher { - /// Target port - #[serde(rename = "TargetPort")] - pub target_port: u16, - /// Published port - #[serde(rename = "PublishedPort")] - pub published_port: Option, - /// Protocol - #[serde(rename = "Protocol")] - pub protocol: String, -} - -/// Result from compose ps command -#[derive(Debug, Clone)] -pub struct ComposePsResult { - /// Raw command output - pub output: ComposeOutput, - /// Parsed container information (if JSON format) - pub containers: Vec, -} - -impl ComposePsResult { - /// Check if the command was successful - #[must_use] - pub fn success(&self) -> bool { - self.output.success - } - - /// Get container information - #[must_use] - pub fn containers(&self) -> &[ComposeContainerInfo] { - &self.containers - } - - /// Get container IDs from output - #[must_use] - pub fn container_ids(&self) -> Vec { - if self.containers.is_empty() { - // Parse from text output if not JSON - self.output - .stdout_lines() - .into_iter() - .skip(1) // Skip header - .filter_map(|line| line.split_whitespace().next()) - .map(String::from) - .collect() - } else { - self.containers.iter().map(|c| c.id.clone()).collect() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_ps_basic() { - let cmd = ComposePsCommand::new(); - let args = cmd.build_args(); - assert!(args.is_empty()); - } - - #[test] - fn test_compose_ps_all() { - let cmd = ComposePsCommand::new().all(); - let args = cmd.build_args(); - assert_eq!(args, vec!["--all"]); - } - - #[test] - fn test_compose_ps_with_format() { - let cmd = ComposePsCommand::new().format("json").all(); - let args = cmd.build_args(); - assert_eq!(args, vec!["--all", "--format", "json"]); - } - - #[test] - fn test_compose_ps_with_filters() { - let cmd = ComposePsCommand::new() - .filter("status=running") - .quiet() - .service("web"); - let args = cmd.build_args(); - assert_eq!(args, vec!["--quiet", "--filter", "status=running", "web"]); - } - - #[test] - fn test_container_status_display() { - assert_eq!(ContainerStatus::Running.to_string(), "running"); - assert_eq!(ContainerStatus::Stopped.to_string(), "stopped"); - assert_eq!(ContainerStatus::Paused.to_string(), "paused"); - assert_eq!(ContainerStatus::Restarting.to_string(), "restarting"); - } -} diff --git a/src/compose/push.rs b/src/compose/push.rs deleted file mode 100644 index c230c506..00000000 --- a/src/compose/push.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! Docker Compose push command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose push command -/// -/// Push service images to registry. -#[derive(Debug, Clone, Default)] -pub struct ComposePushCommand { - /// Base configuration - pub config: ComposeConfig, - /// Include all tags when pushing - pub include_deps: bool, - /// Ignore push failures - pub ignore_push_failures: bool, - /// Don't print progress - pub quiet: bool, - /// Services to push - pub services: Vec, -} - -/// Result from push command -#[derive(Debug, Clone)] -pub struct PushResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposePushCommand { - /// Create a new push command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Include all dependencies - #[must_use] - pub fn include_deps(mut self) -> Self { - self.include_deps = true; - self - } - - /// Ignore push failures - #[must_use] - pub fn ignore_push_failures(mut self) -> Self { - self.ignore_push_failures = true; - self - } - - /// Quiet mode - #[must_use] - pub fn quiet(mut self) -> Self { - self.quiet = true; - self - } - - /// Add a service to push - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to push - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["push".to_string()]; - - // Add flags - if self.include_deps { - args.push("--include-deps".to_string()); - } - if self.ignore_push_failures { - args.push("--ignore-push-failures".to_string()); - } - if self.quiet { - args.push("--quiet".to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposePushCommand { - type Output = PushResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(PushResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_push_command_basic() { - let cmd = ComposePushCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "push"); - } - - #[test] - fn test_push_command_with_flags() { - let cmd = ComposePushCommand::new() - .include_deps() - .ignore_push_failures() - .quiet(); - let args = cmd.build_args(); - assert!(args.contains(&"--include-deps".to_string())); - assert!(args.contains(&"--ignore-push-failures".to_string())); - assert!(args.contains(&"--quiet".to_string())); - } - - #[test] - fn test_push_command_with_services() { - let cmd = ComposePushCommand::new().service("web").service("api"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"api".to_string())); - } -} diff --git a/src/compose/restart.rs b/src/compose/restart.rs deleted file mode 100644 index 7898e33f..00000000 --- a/src/compose/restart.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Docker Compose restart command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; -use std::time::Duration; - -/// Docker Compose restart command builder -#[derive(Debug, Clone, Default)] -pub struct ComposeRestartCommand { - config: ComposeConfig, - services: Vec, - timeout: Option, -} - -impl ComposeRestartCommand { - /// Create a new compose restart command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Create with a specific configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Default::default() - } - } - - /// Add a service to restart - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Set the timeout for stopping containers - #[must_use] - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = Some(timeout); - self - } - - /// Execute the restart command - /// - /// # Errors - /// - /// Returns an error if the docker compose restart command fails - pub async fn run(&self) -> Result { - self.execute().await - } -} - -#[async_trait] -impl ComposeCommand for ComposeRestartCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "restart" - } - - fn build_args(&self) -> Vec { - let mut args = Vec::new(); - if let Some(timeout) = self.timeout { - args.push("--timeout".to_string()); - args.push(timeout.as_secs().to_string()); - } - args.extend(self.services.clone()); - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_restart_basic() { - let cmd = ComposeRestartCommand::new(); - assert_eq!(cmd.subcommand(), "restart"); - assert_eq!(cmd.build_args(), Vec::::new()); - } - - #[test] - fn test_compose_restart_with_services() { - let cmd = ComposeRestartCommand::new().service("web").service("db"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["web", "db"]); - } - - #[test] - fn test_compose_restart_with_timeout() { - let cmd = ComposeRestartCommand::new() - .timeout(Duration::from_secs(30)) - .service("app"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "30", "app"]); - } - - #[test] - fn test_compose_restart_with_config() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .project_name("myapp"); - - let cmd = ComposeRestartCommand::with_config(config) - .service("web") - .timeout(Duration::from_secs(10)); - - assert_eq!(cmd.config().project_name, Some("myapp".to_string())); - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "10", "web"]); - } - - #[test] - fn test_compose_restart_builder_pattern() { - let cmd = ComposeRestartCommand::new() - .service("service1") - .service("service2") - .service("service3") - .timeout(Duration::from_secs(60)); - - let args = cmd.build_args(); - assert_eq!( - args, - vec!["--timeout", "60", "service1", "service2", "service3"] - ); - } - - #[test] - fn test_compose_restart_no_timeout() { - let cmd = ComposeRestartCommand::new().service("nginx"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["nginx"]); - } -} diff --git a/src/compose/rm.rs b/src/compose/rm.rs deleted file mode 100644 index 389f7501..00000000 --- a/src/compose/rm.rs +++ /dev/null @@ -1,188 +0,0 @@ -//! Docker Compose rm command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose rm command -/// -/// Remove stopped service containers. -#[derive(Debug, Clone, Default)] -#[allow(clippy::struct_excessive_bools)] -pub struct ComposeRmCommand { - /// Base configuration - pub config: ComposeConfig, - /// Force removal without confirmation - pub force: bool, - /// Stop containers if running - pub stop: bool, - /// Remove volumes - pub volumes: bool, - /// Remove all containers (not just stopped) - pub all: bool, - /// Services to remove - pub services: Vec, -} - -/// Result from rm command -#[derive(Debug, Clone)] -pub struct RmResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeRmCommand { - /// Create a new rm command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Force removal without confirmation - #[must_use] - pub fn force(mut self) -> Self { - self.force = true; - self - } - - /// Stop containers if running - #[must_use] - pub fn stop(mut self) -> Self { - self.stop = true; - self - } - - /// Remove volumes - #[must_use] - pub fn volumes(mut self) -> Self { - self.volumes = true; - self - } - - /// Remove all containers - #[must_use] - pub fn all(mut self) -> Self { - self.all = true; - self - } - - /// Add a service to remove - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to remove - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["rm".to_string()]; - - // Add flags - if self.force { - args.push("--force".to_string()); - } - if self.stop { - args.push("--stop".to_string()); - } - if self.volumes { - args.push("--volumes".to_string()); - } - if self.all { - args.push("--all".to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeRmCommand { - type Output = RmResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(RmResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_rm_command_basic() { - let cmd = ComposeRmCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "rm"); - } - - #[test] - fn test_rm_command_with_force() { - let cmd = ComposeRmCommand::new().force().stop(); - let args = cmd.build_args(); - assert!(args.contains(&"--force".to_string())); - assert!(args.contains(&"--stop".to_string())); - } - - #[test] - fn test_rm_command_with_services() { - let cmd = ComposeRmCommand::new().service("web").service("db"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"db".to_string())); - } - - #[test] - fn test_rm_command_with_volumes() { - let cmd = ComposeRmCommand::new().volumes().all(); - let args = cmd.build_args(); - assert!(args.contains(&"--volumes".to_string())); - assert!(args.contains(&"--all".to_string())); - } -} diff --git a/src/compose/run.rs b/src/compose/run.rs deleted file mode 100644 index 648e9c5d..00000000 --- a/src/compose/run.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Docker Compose run command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose run command builder -#[derive(Debug, Clone, Default)] -#[allow(dead_code)] // Stub implementation -pub struct ComposeRunCommand { - config: ComposeConfig, - service: String, - command: Vec, - detach: bool, - rm: bool, -} - -impl ComposeRunCommand { - /// Create a new compose run command - #[must_use] - pub fn new(service: impl Into) -> Self { - Self { - service: service.into(), - ..Self::default() - } - } - - /// Execute the run command - /// - /// # Errors - /// - /// Returns an error if the docker compose run command fails - pub async fn run(&self) -> Result { - self.execute().await - } -} - -#[async_trait] -impl ComposeCommand for ComposeRunCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "run" - } - - fn build_args(&self) -> Vec { - let mut args = vec![self.service.clone()]; - args.extend(self.command.clone()); - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_run_basic() { - let cmd = ComposeRunCommand::new("web"); - assert_eq!(cmd.service, "web"); - assert_eq!(cmd.subcommand(), "run"); - let args = cmd.build_args(); - assert_eq!(args, vec!["web"]); - } - - #[test] - fn test_compose_run_with_command() { - let mut cmd = ComposeRunCommand::new("worker"); - cmd.command = vec!["python".to_string(), "script.py".to_string()]; - - let args = cmd.build_args(); - assert_eq!(args, vec!["worker", "python", "script.py"]); - } - - #[test] - fn test_compose_run_with_config() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .project_name("myproject"); - - let mut cmd = ComposeRunCommand::new("test"); - cmd.config = config; - cmd.command = vec!["npm".to_string(), "test".to_string()]; - - assert_eq!(cmd.config().project_name, Some("myproject".to_string())); - assert_eq!(cmd.build_args(), vec!["test", "npm", "test"]); - } - - #[test] - fn test_compose_run_future_options() { - // Test that fields exist for future implementation - let cmd = ComposeRunCommand { - config: ComposeConfig::new(), - service: "app".to_string(), - command: vec!["echo".to_string(), "hello".to_string()], - detach: true, - rm: true, - }; - - // Currently only includes service and command - let args = cmd.build_args(); - assert_eq!(args, vec!["app", "echo", "hello"]); - - // When fully implemented, it should include options: - // assert!(args.contains(&"--detach".to_string())); - // assert!(args.contains(&"--rm".to_string())); - } -} diff --git a/src/compose/scale.rs b/src/compose/scale.rs deleted file mode 100644 index 49db7257..00000000 --- a/src/compose/scale.rs +++ /dev/null @@ -1,157 +0,0 @@ -//! Docker Compose scale command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; -use std::collections::HashMap; - -/// Docker Compose scale command -/// -/// Scale services to specific number of instances. -#[derive(Debug, Clone, Default)] -pub struct ComposeScaleCommand { - /// Base configuration - pub config: ComposeConfig, - /// Service scale specifications (service=num) - pub scales: HashMap, - /// Don't start new containers - pub no_deps: bool, -} - -/// Result from scale command -#[derive(Debug, Clone)] -pub struct ScaleResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeScaleCommand { - /// Create a new scale command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Scale a service to a specific number of instances - #[must_use] - pub fn scale(mut self, service: impl Into, instances: u32) -> Self { - self.scales.insert(service.into(), instances); - self - } - - /// Scale multiple services - #[must_use] - pub fn scales(mut self, scales: I) -> Self - where - I: IntoIterator, - S: Into, - { - for (service, count) in scales { - self.scales.insert(service.into(), count); - } - self - } - - /// Don't start dependency services - #[must_use] - pub fn no_deps(mut self) -> Self { - self.no_deps = true; - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["scale".to_string()]; - - // Add flags - if self.no_deps { - args.push("--no-deps".to_string()); - } - - // Add service scales - for (service, count) in &self.scales { - args.push(format!("{service}={count}")); - } - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeScaleCommand { - type Output = ScaleResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(ScaleResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_scale_command_basic() { - let cmd = ComposeScaleCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "scale"); - } - - #[test] - fn test_scale_command_with_service() { - let cmd = ComposeScaleCommand::new() - .scale("web", 3) - .scale("worker", 5); - let args = cmd.build_args(); - assert!(args.iter().any(|arg| arg == "web=3" || arg == "worker=5")); - } - - #[test] - fn test_scale_command_with_no_deps() { - let cmd = ComposeScaleCommand::new().scale("web", 2).no_deps(); - let args = cmd.build_args(); - assert!(args.contains(&"--no-deps".to_string())); - assert!(args.iter().any(|arg| arg == "web=2")); - } - - #[test] - fn test_scale_command_with_multiple() { - let scales = vec![("app", 4), ("cache", 2)]; - let cmd = ComposeScaleCommand::new().scales(scales); - let args = cmd.build_args(); - assert!(args.iter().any(|arg| arg == "app=4" || arg == "cache=2")); - } -} diff --git a/src/compose/start.rs b/src/compose/start.rs deleted file mode 100644 index 6998578e..00000000 --- a/src/compose/start.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Docker Compose start command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose start command builder -#[derive(Debug, Clone, Default)] -pub struct ComposeStartCommand { - config: ComposeConfig, - services: Vec, -} - -impl ComposeStartCommand { - /// Create a new compose start command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Create with a specific configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Default::default() - } - } - - /// Add a service to start - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Execute the start command - /// - /// # Errors - /// - /// Returns an error if the docker compose start command fails - pub async fn run(&self) -> Result { - self.execute().await - } -} - -#[async_trait] -impl ComposeCommand for ComposeStartCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "start" - } - - fn build_args(&self) -> Vec { - self.services.clone() - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_start_basic() { - let cmd = ComposeStartCommand::new(); - assert_eq!(cmd.subcommand(), "start"); - assert_eq!(cmd.build_args(), Vec::::new()); - } - - #[test] - fn test_compose_start_with_services() { - let cmd = ComposeStartCommand::new() - .service("web") - .service("db") - .service("cache"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["web", "db", "cache"]); - } - - #[test] - fn test_compose_start_with_config() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .project_name("myapp") - .profile("production"); - - let cmd = ComposeStartCommand::with_config(config).service("api"); - - assert_eq!(cmd.config().project_name, Some("myapp".to_string())); - assert_eq!(cmd.config().profiles, vec!["production"]); - assert_eq!(cmd.build_args(), vec!["api"]); - } - - #[test] - fn test_compose_start_single_service() { - let cmd = ComposeStartCommand::new().service("postgres"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["postgres"]); - } - - #[test] - fn test_compose_start_no_services() { - // Starting all services when no specific services are specified - let cmd = ComposeStartCommand::new(); - - let args = cmd.build_args(); - assert!(args.is_empty()); - } - - #[test] - fn test_compose_start_builder_pattern() { - let cmd = ComposeStartCommand::with_config(ComposeConfig::new().project_name("test")) - .service("frontend") - .service("backend"); - - assert_eq!(cmd.services.len(), 2); - assert!(cmd.services.contains(&"frontend".to_string())); - assert!(cmd.services.contains(&"backend".to_string())); - } -} diff --git a/src/compose/stop.rs b/src/compose/stop.rs deleted file mode 100644 index 4d8ead38..00000000 --- a/src/compose/stop.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Docker Compose stop command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; -use std::time::Duration; - -/// Docker Compose stop command builder -#[derive(Debug, Clone, Default)] -pub struct ComposeStopCommand { - config: ComposeConfig, - services: Vec, - timeout: Option, -} - -impl ComposeStopCommand { - /// Create a new compose stop command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Create with a specific configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Default::default() - } - } - - /// Add a service to stop - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Set the timeout for stopping containers - #[must_use] - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = Some(timeout); - self - } - - /// Execute the stop command - /// - /// # Errors - /// - /// Returns an error if the docker compose stop command fails - pub async fn run(&self) -> Result { - self.execute().await - } -} - -#[async_trait] -impl ComposeCommand for ComposeStopCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "stop" - } - - fn build_args(&self) -> Vec { - let mut args = Vec::new(); - if let Some(timeout) = self.timeout { - args.push("--timeout".to_string()); - args.push(timeout.as_secs().to_string()); - } - args.extend(self.services.clone()); - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_stop_basic() { - let cmd = ComposeStopCommand::new(); - assert_eq!(cmd.subcommand(), "stop"); - assert_eq!(cmd.build_args(), Vec::::new()); - } - - #[test] - fn test_compose_stop_with_services() { - let cmd = ComposeStopCommand::new().service("web").service("worker"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["web", "worker"]); - } - - #[test] - fn test_compose_stop_with_timeout() { - let cmd = ComposeStopCommand::new() - .timeout(Duration::from_secs(10)) - .service("db"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "10", "db"]); - } - - #[test] - fn test_compose_stop_with_config() { - let config = ComposeConfig::new() - .file("compose.yml") - .project_name("webapp"); - - let cmd = ComposeStopCommand::with_config(config) - .timeout(Duration::from_secs(5)) - .service("redis") - .service("postgres"); - - assert_eq!(cmd.config().project_name, Some("webapp".to_string())); - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "5", "redis", "postgres"]); - } - - #[test] - fn test_compose_stop_zero_timeout() { - let cmd = ComposeStopCommand::new() - .timeout(Duration::from_secs(0)) - .service("app"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "0", "app"]); - } - - #[test] - fn test_compose_stop_long_timeout() { - let cmd = ComposeStopCommand::new() - .timeout(Duration::from_secs(300)) - .service("slow-service"); - - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "300", "slow-service"]); - } - - #[test] - fn test_compose_stop_all_services() { - // Stop all services when no specific services are specified - let cmd = ComposeStopCommand::new().timeout(Duration::from_secs(30)); - - let args = cmd.build_args(); - assert_eq!(args, vec!["--timeout", "30"]); - } -} diff --git a/src/compose/top.rs b/src/compose/top.rs deleted file mode 100644 index 4b9770fa..00000000 --- a/src/compose/top.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Docker Compose top command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose top command -/// -/// Display running processes of a service. -#[derive(Debug, Clone, Default)] -pub struct ComposeTopCommand { - /// Base configuration - pub config: ComposeConfig, - /// Services to show processes for - pub services: Vec, -} - -/// Result from top command -#[derive(Debug, Clone)] -pub struct TopResult { - /// Output from the command (process list) - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeTopCommand { - /// Create a new top command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Add a service to show processes for - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["top".to_string()]; - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeTopCommand { - type Output = TopResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(TopResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_top_command_basic() { - let cmd = ComposeTopCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "top"); - } - - #[test] - fn test_top_command_with_services() { - let cmd = ComposeTopCommand::new().service("web").service("worker"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"worker".to_string())); - } -} diff --git a/src/compose/unpause.rs b/src/compose/unpause.rs deleted file mode 100644 index 74b4bfda..00000000 --- a/src/compose/unpause.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! Docker Compose unpause command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose unpause command -/// -/// Unpause services. -#[derive(Debug, Clone, Default)] -pub struct ComposeUnpauseCommand { - /// Base configuration - pub config: ComposeConfig, - /// Services to unpause - pub services: Vec, -} - -/// Result from unpause command -#[derive(Debug, Clone)] -pub struct UnpauseResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeUnpauseCommand { - /// Create a new unpause command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Add a service to unpause - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to unpause - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["unpause".to_string()]; - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeUnpauseCommand { - type Output = UnpauseResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(UnpauseResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_unpause_command_basic() { - let cmd = ComposeUnpauseCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "unpause"); - } - - #[test] - fn test_unpause_command_with_services() { - let cmd = ComposeUnpauseCommand::new() - .service("web") - .service("worker"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"worker".to_string())); - } -} diff --git a/src/compose/up.rs b/src/compose/up.rs deleted file mode 100644 index b4e09359..00000000 --- a/src/compose/up.rs +++ /dev/null @@ -1,483 +0,0 @@ -//! Docker Compose up command implementation. - -use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput}; -use crate::error::Result; -use async_trait::async_trait; -use std::time::Duration; - -/// Docker Compose up command builder -#[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are needed for compose up options -pub struct ComposeUpCommand { - /// Base compose configuration - config: ComposeConfig, - /// Services to start (empty for all) - services: Vec, - /// Run in detached mode - detach: bool, - /// Don't start linked services - no_deps: bool, - /// Force recreate containers - force_recreate: bool, - /// Recreate containers even if configuration unchanged - always_recreate_deps: bool, - /// Don't recreate containers - no_recreate: bool, - /// Don't build images - no_build: bool, - /// Don't start services - no_start: bool, - /// Build images before starting - build: bool, - /// Remove orphan containers - remove_orphans: bool, - /// Scale SERVICE to NUM instances - scale: Vec<(String, u32)>, - /// Timeout for container shutdown - timeout: Option, - /// Exit code from first container that stops - exit_code_from: Option, - /// Abort if containers are stopped - abort_on_container_exit: bool, - /// Attach to dependent containers - attach_dependencies: bool, - /// Recreate anonymous volumes - renew_anon_volumes: bool, - /// Wait for services to be healthy - wait: bool, - /// Maximum wait timeout - wait_timeout: Option, - /// Pull image policy - pull: Option, -} - -/// Image pull policy -#[derive(Debug, Clone, Copy)] -pub enum PullPolicy { - /// Always pull images - Always, - /// Never pull images - Never, - /// Pull missing images (default) - Missing, -} - -impl std::fmt::Display for PullPolicy { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Always => write!(f, "always"), - Self::Never => write!(f, "never"), - Self::Missing => write!(f, "missing"), - } - } -} - -impl ComposeUpCommand { - /// Create a new compose up command - #[must_use] - pub fn new() -> Self { - Self { - config: ComposeConfig::new(), - services: Vec::new(), - detach: false, - no_deps: false, - force_recreate: false, - always_recreate_deps: false, - no_recreate: false, - no_build: false, - no_start: false, - build: false, - remove_orphans: false, - scale: Vec::new(), - timeout: None, - exit_code_from: None, - abort_on_container_exit: false, - attach_dependencies: false, - renew_anon_volumes: false, - wait: false, - wait_timeout: None, - pull: None, - } - } - - /// Create with a specific compose configuration - #[must_use] - pub fn with_config(config: ComposeConfig) -> Self { - Self { - config, - ..Self::new() - } - } - - /// Add a service to start - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - /// Run in detached mode - #[must_use] - pub fn detach(mut self) -> Self { - self.detach = true; - self - } - - /// Don't start linked services - #[must_use] - pub fn no_deps(mut self) -> Self { - self.no_deps = true; - self - } - - /// Force recreate containers - #[must_use] - pub fn force_recreate(mut self) -> Self { - self.force_recreate = true; - self - } - - /// Always recreate dependent containers - #[must_use] - pub fn always_recreate_deps(mut self) -> Self { - self.always_recreate_deps = true; - self - } - - /// Don't recreate containers - #[must_use] - pub fn no_recreate(mut self) -> Self { - self.no_recreate = true; - self - } - - /// Don't build images - #[must_use] - pub fn no_build(mut self) -> Self { - self.no_build = true; - self - } - - /// Don't start services after creating - #[must_use] - pub fn no_start(mut self) -> Self { - self.no_start = true; - self - } - - /// Build images before starting - #[must_use] - pub fn build(mut self) -> Self { - self.build = true; - self - } - - /// Remove orphan containers - #[must_use] - pub fn remove_orphans(mut self) -> Self { - self.remove_orphans = true; - self - } - - /// Scale a service to a specific number of instances - #[must_use] - pub fn scale(mut self, service: impl Into, instances: u32) -> Self { - self.scale.push((service.into(), instances)); - self - } - - /// Set timeout for container shutdown - #[must_use] - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = Some(timeout); - self - } - - /// Use exit code from specific container - #[must_use] - pub fn exit_code_from(mut self, service: impl Into) -> Self { - self.exit_code_from = Some(service.into()); - self - } - - /// Abort when containers stop - #[must_use] - pub fn abort_on_container_exit(mut self) -> Self { - self.abort_on_container_exit = true; - self - } - - /// Attach to dependent containers - #[must_use] - pub fn attach_dependencies(mut self) -> Self { - self.attach_dependencies = true; - self - } - - /// Recreate anonymous volumes - #[must_use] - pub fn renew_anon_volumes(mut self) -> Self { - self.renew_anon_volumes = true; - self - } - - /// Wait for services to be running/healthy - #[must_use] - pub fn wait(mut self) -> Self { - self.wait = true; - self - } - - /// Set maximum wait timeout - #[must_use] - pub fn wait_timeout(mut self, timeout: Duration) -> Self { - self.wait_timeout = Some(timeout); - self - } - - /// Set pull policy - #[must_use] - pub fn pull(mut self, policy: PullPolicy) -> Self { - self.pull = Some(policy); - self - } - - /// Set compose file - #[must_use] - pub fn file(mut self, path: impl Into) -> Self { - self.config = self.config.file(path); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config = self.config.project_name(name); - self - } - - /// Execute the compose up command - /// - /// # Errors - /// Returns an error if: - /// - Docker Compose is not installed - /// - Compose file is not found or invalid - /// - Service configuration errors - /// - Container creation/start fails - pub async fn run(&self) -> Result { - let output = self.execute().await?; - - Ok(ComposeUpResult { - output, - services: self.services.clone(), - detached: self.detach, - }) - } -} - -impl Default for ComposeUpCommand { - fn default() -> Self { - Self::new() - } -} - -#[async_trait] -impl ComposeCommand for ComposeUpCommand { - type Output = ComposeOutput; - - fn subcommand(&self) -> &'static str { - "up" - } - - fn build_args(&self) -> Vec { - let mut args = Vec::new(); - - if self.detach { - args.push("--detach".to_string()); - } - - if self.no_deps { - args.push("--no-deps".to_string()); - } - - if self.force_recreate { - args.push("--force-recreate".to_string()); - } - - if self.always_recreate_deps { - args.push("--always-recreate-deps".to_string()); - } - - if self.no_recreate { - args.push("--no-recreate".to_string()); - } - - if self.no_build { - args.push("--no-build".to_string()); - } - - if self.no_start { - args.push("--no-start".to_string()); - } - - if self.build { - args.push("--build".to_string()); - } - - if self.remove_orphans { - args.push("--remove-orphans".to_string()); - } - - for (service, count) in &self.scale { - args.push("--scale".to_string()); - args.push(format!("{service}={count}")); - } - - if let Some(timeout) = self.timeout { - args.push("--timeout".to_string()); - args.push(timeout.as_secs().to_string()); - } - - if let Some(ref service) = self.exit_code_from { - args.push("--exit-code-from".to_string()); - args.push(service.clone()); - } - - if self.abort_on_container_exit { - args.push("--abort-on-container-exit".to_string()); - } - - if self.attach_dependencies { - args.push("--attach-dependencies".to_string()); - } - - if self.renew_anon_volumes { - args.push("--renew-anon-volumes".to_string()); - } - - if self.wait { - args.push("--wait".to_string()); - } - - if let Some(timeout) = self.wait_timeout { - args.push("--wait-timeout".to_string()); - args.push(timeout.as_secs().to_string()); - } - - if let Some(ref pull) = self.pull { - args.push("--pull".to_string()); - args.push(pull.to_string()); - } - - // Add service names at the end - args.extend(self.services.clone()); - - args - } - - async fn execute(&self) -> Result { - execute_compose_command(&self.config, self.subcommand(), self.build_args()).await - } - - fn config(&self) -> &ComposeConfig { - &self.config - } -} - -/// Result from compose up command -#[derive(Debug, Clone)] -pub struct ComposeUpResult { - /// Raw command output - pub output: ComposeOutput, - /// Services that were started - pub services: Vec, - /// Whether running in detached mode - pub detached: bool, -} - -impl ComposeUpResult { - /// Check if the command was successful - #[must_use] - pub fn success(&self) -> bool { - self.output.success - } - - /// Get the services that were started - #[must_use] - pub fn services(&self) -> &[String] { - &self.services - } - - /// Check if running in detached mode - #[must_use] - pub fn is_detached(&self) -> bool { - self.detached - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_up_basic() { - let cmd = ComposeUpCommand::new(); - let args = cmd.build_args(); - assert!(args.is_empty()); - } - - #[test] - fn test_compose_up_detached() { - let cmd = ComposeUpCommand::new().detach(); - let args = cmd.build_args(); - assert_eq!(args, vec!["--detach"]); - } - - #[test] - fn test_compose_up_with_services() { - let cmd = ComposeUpCommand::new().service("web").service("db"); - let args = cmd.build_args(); - assert_eq!(args, vec!["web", "db"]); - } - - #[test] - fn test_compose_up_all_options() { - let cmd = ComposeUpCommand::new() - .detach() - .build() - .remove_orphans() - .scale("web", 3) - .wait() - .pull(PullPolicy::Always) - .service("web") - .service("db"); - - let args = cmd.build_args(); - assert!(args.contains(&"--detach".to_string())); - assert!(args.contains(&"--build".to_string())); - assert!(args.contains(&"--remove-orphans".to_string())); - assert!(args.contains(&"--scale".to_string())); - assert!(args.contains(&"web=3".to_string())); - assert!(args.contains(&"--wait".to_string())); - assert!(args.contains(&"--pull".to_string())); - assert!(args.contains(&"always".to_string())); - } - - #[test] - fn test_pull_policy_display() { - assert_eq!(PullPolicy::Always.to_string(), "always"); - assert_eq!(PullPolicy::Never.to_string(), "never"); - assert_eq!(PullPolicy::Missing.to_string(), "missing"); - } -} diff --git a/src/compose/version.rs b/src/compose/version.rs deleted file mode 100644 index 5060d124..00000000 --- a/src/compose/version.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Docker Compose version command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; -use serde::Deserialize; - -/// Docker Compose version command -/// -/// Show Docker Compose version information. -#[derive(Debug, Clone, Default)] -pub struct ComposeVersionCommand { - /// Base configuration - pub config: ComposeConfig, - /// Format output (pretty, json) - pub format: Option, - /// Short output - pub short: bool, -} - -/// Version output format -#[derive(Debug, Clone, Copy)] -pub enum VersionFormat { - /// Pretty format (default) - Pretty, - /// JSON format - Json, -} - -impl VersionFormat { - /// Convert to command line argument - #[must_use] - pub fn as_arg(&self) -> &str { - match self { - Self::Pretty => "pretty", - Self::Json => "json", - } - } -} - -/// Version information -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct VersionInfo { - /// Compose version - pub version: String, -} - -/// Result from version command -#[derive(Debug, Clone)] -pub struct VersionResult { - /// Version information - pub info: Option, - /// Raw output - pub raw_output: String, -} - -impl ComposeVersionCommand { - /// Create a new version command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Set output format - #[must_use] - pub fn format(mut self, format: VersionFormat) -> Self { - self.format = Some(format); - self - } - - /// Set output format to JSON - #[must_use] - pub fn format_json(mut self) -> Self { - self.format = Some(VersionFormat::Json); - self - } - - /// Enable short output - #[must_use] - pub fn short(mut self) -> Self { - self.short = true; - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["version".to_string()]; - - // Add flags - if self.short { - args.push("--short".to_string()); - } - - // Add format - if let Some(format) = &self.format { - args.push("--format".to_string()); - args.push(format.as_arg().to_string()); - } - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeVersionCommand { - type Output = VersionResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - // Parse JSON output if format is JSON - let info = if matches!(self.format, Some(VersionFormat::Json)) { - serde_json::from_str(&output.stdout).ok() - } else { - None - }; - - Ok(VersionResult { - info, - raw_output: output.stdout, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_version_command_basic() { - let cmd = ComposeVersionCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "version"); - } - - #[test] - fn test_version_command_with_format() { - let cmd = ComposeVersionCommand::new().format_json(); - let args = cmd.build_args(); - assert!(args.contains(&"--format".to_string())); - assert!(args.contains(&"json".to_string())); - } - - #[test] - fn test_version_command_with_short() { - let cmd = ComposeVersionCommand::new().short(); - let args = cmd.build_args(); - assert!(args.contains(&"--short".to_string())); - } -} diff --git a/src/compose/wait.rs b/src/compose/wait.rs deleted file mode 100644 index 19b91231..00000000 --- a/src/compose/wait.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Docker Compose wait command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose wait command -/// -/// Wait for services to reach a desired state. -#[derive(Debug, Clone, Default)] -pub struct ComposeWaitCommand { - /// Base configuration - pub config: ComposeConfig, - /// Services to wait for - pub services: Vec, - /// Wait for services to be running and healthy - pub down_project: bool, -} - -/// Result from wait command -#[derive(Debug, Clone)] -pub struct WaitResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, - /// Exit codes from services - pub exit_codes: Vec, -} - -impl ComposeWaitCommand { - /// Create a new wait command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Wait for the entire project to stop - #[must_use] - pub fn down_project(mut self) -> Self { - self.down_project = true; - self - } - - /// Add a service to wait for - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to wait for - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["wait".to_string()]; - - // Add flags - if self.down_project { - args.push("--down-project".to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeWaitCommand { - type Output = WaitResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - // Parse exit codes from output if available - let exit_codes = output - .stdout - .lines() - .filter_map(|line| { - // Try to parse exit codes from output - line.split_whitespace() - .last() - .and_then(|s| s.parse::().ok()) - }) - .collect(); - - Ok(WaitResult { - output: output.stdout, - success: output.success, - exit_codes, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -impl WaitResult { - /// Check if all services exited successfully (code 0) - #[must_use] - pub fn all_successful(&self) -> bool { - !self.exit_codes.is_empty() && self.exit_codes.iter().all(|&code| code == 0) - } - - /// Get the first non-zero exit code - #[must_use] - pub fn first_failure(&self) -> Option { - self.exit_codes.iter().find(|&&code| code != 0).copied() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_wait_command_basic() { - let cmd = ComposeWaitCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "wait"); - } - - #[test] - fn test_wait_command_with_services() { - let cmd = ComposeWaitCommand::new().service("web").service("db"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"db".to_string())); - } - - #[test] - fn test_wait_command_with_down_project() { - let cmd = ComposeWaitCommand::new().down_project(); - let args = cmd.build_args(); - assert!(args.contains(&"--down-project".to_string())); - } - - #[test] - fn test_wait_result_helpers() { - let result = WaitResult { - output: String::new(), - success: true, - exit_codes: vec![0, 0, 0], - }; - assert!(result.all_successful()); - assert_eq!(result.first_failure(), None); - - let result_with_failure = WaitResult { - output: String::new(), - success: false, - exit_codes: vec![0, 1, 0], - }; - assert!(!result_with_failure.all_successful()); - assert_eq!(result_with_failure.first_failure(), Some(1)); - } -} diff --git a/src/compose/watch.rs b/src/compose/watch.rs deleted file mode 100644 index cc678daf..00000000 --- a/src/compose/watch.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! Docker Compose watch command implementation. - -use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig}; -use crate::error::Result; -use async_trait::async_trait; - -/// Docker Compose watch command -/// -/// Watch build context for changes and rebuild/restart services automatically. -#[derive(Debug, Clone, Default)] -pub struct ComposeWatchCommand { - /// Base configuration - pub config: ComposeConfig, - /// Don't build images - pub no_up: bool, - /// Services to watch - pub services: Vec, -} - -/// Result from watch command -#[derive(Debug, Clone)] -pub struct WatchResult { - /// Output from the command - pub output: String, - /// Whether the operation succeeded - pub success: bool, -} - -impl ComposeWatchCommand { - /// Create a new watch command - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Add a compose file - #[must_use] - pub fn file>(mut self, file: P) -> Self { - self.config.files.push(file.into()); - self - } - - /// Set project name - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.config.project_name = Some(name.into()); - self - } - - /// Don't start services before watching - #[must_use] - pub fn no_up(mut self) -> Self { - self.no_up = true; - self - } - - /// Add a service to watch - #[must_use] - pub fn service(mut self, service: impl Into) -> Self { - self.services.push(service.into()); - self - } - - /// Add multiple services to watch - #[must_use] - pub fn services(mut self, services: I) -> Self - where - I: IntoIterator, - S: Into, - { - self.services.extend(services.into_iter().map(Into::into)); - self - } - - fn build_args(&self) -> Vec { - let mut args = vec!["watch".to_string()]; - - // Add flags - if self.no_up { - args.push("--no-up".to_string()); - } - - // Add services - args.extend(self.services.clone()); - - args - } -} - -#[async_trait] -impl ComposeCommand for ComposeWatchCommand { - type Output = WatchResult; - - fn get_config(&self) -> &ComposeConfig { - &self.config - } - - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config - } - - async fn execute_compose(&self, args: Vec) -> Result { - let output = self.execute_compose_command(args).await?; - - Ok(WatchResult { - output: output.stdout, - success: output.success, - }) - } - - async fn execute(&self) -> Result { - let args = self.build_args(); - self.execute_compose(args).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_watch_command_basic() { - let cmd = ComposeWatchCommand::new(); - let args = cmd.build_args(); - assert_eq!(args[0], "watch"); - } - - #[test] - fn test_watch_command_with_no_up() { - let cmd = ComposeWatchCommand::new().no_up(); - let args = cmd.build_args(); - assert!(args.contains(&"--no-up".to_string())); - } - - #[test] - fn test_watch_command_with_services() { - let cmd = ComposeWatchCommand::new().service("web").service("worker"); - let args = cmd.build_args(); - assert!(args.contains(&"web".to_string())); - assert!(args.contains(&"worker".to_string())); - } -} From 5c504be143dd656e9739b826d5062ed0d4b87b90 Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 12:19:47 +0800 Subject: [PATCH 4/9] feat: `build.rs` --- src/command/build.rs | 134 ++++++++++++++++++----------------- src/command/builder/build.rs | 7 +- 2 files changed, 73 insertions(+), 68 deletions(-) diff --git a/src/command/build.rs b/src/command/build.rs index a53e4ce7..4f2de7b3 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -1,7 +1,7 @@ -//! Docker build command implementation. +//! Implements the `docker build` command. //! -//! This module provides a comprehensive implementation of the `docker build` command -//! with support for all native options and an extensible architecture for any additional options. +//! Provides a comprehensive implementation of the `docker build` command with support for +//! all native options and an extensible architecture for additional options. use super::{CommandExecutor, DockerCommand}; use crate::error::Result; @@ -12,7 +12,7 @@ use std::path::PathBuf; use tokio::process::Command as TokioCommand; use tokio::sync::mpsc; -/// Docker build command builder with fluent API +/// Provides a Docker build command builder with a fluent API. #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub struct BuildCommand { @@ -110,7 +110,7 @@ pub struct BuildCommand { ssh: Vec, } -/// Output from docker build command +/// Represents output from the docker build command. #[derive(Debug, Clone)] pub struct BuildOutput { /// The raw stdout from the command @@ -124,13 +124,13 @@ pub struct BuildOutput { } impl BuildOutput { - /// Check if the build executed successfully + /// Checks if the build executed successfully. #[must_use] pub fn success(&self) -> bool { self.exit_code == 0 } - /// Get combined output (stdout + stderr) + /// Gets the combined output (stdout + stderr). #[must_use] pub fn combined_output(&self) -> String { if self.stderr.is_empty() { @@ -142,19 +142,19 @@ impl BuildOutput { } } - /// Check if stdout is empty (ignoring whitespace) + /// Checks if stdout is empty (ignoring whitespace). #[must_use] pub fn stdout_is_empty(&self) -> bool { self.stdout.trim().is_empty() } - /// Check if stderr is empty (ignoring whitespace) + /// Checks if stderr is empty (ignoring whitespace). #[must_use] pub fn stderr_is_empty(&self) -> bool { self.stderr.trim().is_empty() } - /// Extract image ID from build output (best effort) + /// Extracts the image ID from build output (best effort). fn extract_image_id(output: &str) -> Option { // Look for patterns like "Successfully built abc123def456" or "sha256:..." for line in output.lines() { @@ -172,7 +172,7 @@ impl BuildOutput { } impl BuildCommand { - /// Create a new build command for the specified context + /// Creates a new build command for the specified context. /// /// # Examples /// @@ -234,7 +234,7 @@ impl BuildCommand { } } - /// Add a custom host-to-IP mapping + /// Adds a custom host-to-IP mapping. /// /// # Examples /// @@ -250,7 +250,7 @@ impl BuildCommand { self } - /// Set a build-time variable + /// Sets a build-time variable. /// /// # Examples /// @@ -267,7 +267,7 @@ impl BuildCommand { self } - /// Set multiple build-time variables + /// Sets multiple build-time variables. /// /// # Examples /// @@ -287,7 +287,7 @@ impl BuildCommand { self } - /// Add an image to consider as cache source + /// Adds an image to consider as a cache source. /// /// # Examples /// @@ -303,7 +303,7 @@ impl BuildCommand { self } - /// Set the parent cgroup for RUN instructions + /// Sets the parent cgroup for RUN instructions. /// /// # Examples /// @@ -319,7 +319,7 @@ impl BuildCommand { self } - /// Compress the build context using gzip + /// Compresses the build context using gzip. /// /// # Examples /// @@ -334,7 +334,7 @@ impl BuildCommand { self } - /// Set CPU period limit + /// Sets the CPU period limit. /// /// # Examples /// @@ -350,7 +350,7 @@ impl BuildCommand { self } - /// Set CPU quota limit + /// Sets the CPU quota limit. /// /// # Examples /// @@ -366,7 +366,7 @@ impl BuildCommand { self } - /// Set CPU shares (relative weight) + /// Sets CPU shares (relative weight). /// /// # Examples /// @@ -382,7 +382,7 @@ impl BuildCommand { self } - /// Set CPUs in which to allow execution + /// Sets CPUs in which to allow execution. /// /// # Examples /// @@ -398,7 +398,7 @@ impl BuildCommand { self } - /// Set MEMs in which to allow execution + /// Sets MEMs in which to allow execution. /// /// # Examples /// @@ -414,7 +414,7 @@ impl BuildCommand { self } - /// Skip image verification + /// Skips image verification. /// /// # Examples /// @@ -430,7 +430,7 @@ impl BuildCommand { self } - /// Set the name/path of the Dockerfile + /// Sets the name/path of the Dockerfile. /// /// # Examples /// @@ -446,7 +446,7 @@ impl BuildCommand { self } - /// Always remove intermediate containers + /// Always removes intermediate containers. /// /// # Examples /// @@ -462,7 +462,7 @@ impl BuildCommand { self } - /// Write the image ID to a file + /// Writes the image ID to a file. /// /// # Examples /// @@ -478,7 +478,7 @@ impl BuildCommand { self } - /// Set container isolation technology + /// Sets the container isolation technology. /// /// # Examples /// @@ -494,7 +494,7 @@ impl BuildCommand { self } - /// Set metadata label for the image + /// Sets a metadata label for the image. /// /// # Examples /// @@ -511,7 +511,7 @@ impl BuildCommand { self } - /// Set multiple metadata labels + /// Sets multiple metadata labels. /// /// # Examples /// @@ -531,7 +531,7 @@ impl BuildCommand { self } - /// Set memory limit + /// Sets the memory limit. /// /// # Examples /// @@ -547,7 +547,7 @@ impl BuildCommand { self } - /// Set memory + swap limit + /// Sets the memory + swap limit. /// /// # Examples /// @@ -563,7 +563,7 @@ impl BuildCommand { self } - /// Set networking mode for RUN instructions + /// Sets the networking mode for RUN instructions. /// /// # Examples /// @@ -579,7 +579,7 @@ impl BuildCommand { self } - /// Do not use cache when building + /// Disables cache when building. /// /// # Examples /// @@ -595,7 +595,7 @@ impl BuildCommand { self } - /// Set platform for multi-platform builds + /// Sets the platform for multi-platform builds. /// /// # Examples /// @@ -611,7 +611,7 @@ impl BuildCommand { self } - /// Always attempt to pull newer base images + /// Attempts to pull newer base images. /// /// # Examples /// @@ -627,7 +627,7 @@ impl BuildCommand { self } - /// Suppress build output and print image ID on success + /// Suppresses build output and prints the image ID on success. /// /// # Examples /// @@ -643,7 +643,7 @@ impl BuildCommand { self } - /// Remove intermediate containers after successful build (default: true) + /// Removes intermediate containers after a successful build (default: true). /// /// # Examples /// @@ -659,7 +659,7 @@ impl BuildCommand { self } - /// Add a security option + /// Adds a security option. /// /// # Examples /// @@ -675,7 +675,7 @@ impl BuildCommand { self } - /// Set size of /dev/shm + /// Sets the size of `/dev/shm`. /// /// # Examples /// @@ -691,7 +691,7 @@ impl BuildCommand { self } - /// Add a name and tag for the image + /// Adds a name and tag for the image. /// /// # Examples /// @@ -708,7 +708,7 @@ impl BuildCommand { self } - /// Set multiple tags for the image + /// Sets multiple tags for the image. /// /// # Examples /// @@ -724,7 +724,7 @@ impl BuildCommand { self } - /// Set the target build stage + /// Sets the target build stage. /// /// # Examples /// @@ -740,7 +740,7 @@ impl BuildCommand { self } - /// Add a ulimit option + /// Adds a ulimit option. /// /// # Examples /// @@ -756,7 +756,7 @@ impl BuildCommand { self } - /// Add an extra privileged entitlement + /// Adds an extra privileged entitlement. /// /// # Examples /// @@ -772,7 +772,7 @@ impl BuildCommand { self } - /// Add an annotation to the image + /// Adds an annotation to the image. /// /// # Examples /// @@ -788,7 +788,7 @@ impl BuildCommand { self } - /// Add attestation parameters + /// Adds attestation parameters. /// /// # Examples /// @@ -804,7 +804,7 @@ impl BuildCommand { self } - /// Add additional build context + /// Adds additional build context. /// /// # Examples /// @@ -820,7 +820,7 @@ impl BuildCommand { self } - /// Override the configured builder + /// Overrides the configured builder. /// /// # Examples /// @@ -836,7 +836,7 @@ impl BuildCommand { self } - /// Add cache export destination + /// Adds a cache export destination. /// /// # Examples /// @@ -852,7 +852,7 @@ impl BuildCommand { self } - /// Set method for evaluating build + /// Sets the method for evaluating the build. /// /// # Examples /// @@ -868,7 +868,7 @@ impl BuildCommand { self } - /// Enable check mode (shorthand for "--call=check") + /// Enables check mode (shorthand for "--call=check"). /// /// # Examples /// @@ -884,7 +884,7 @@ impl BuildCommand { self } - /// Enable load mode (shorthand for "--output=type=docker") + /// Enables load mode (shorthand for "--output=type=docker"). /// /// # Examples /// @@ -900,7 +900,7 @@ impl BuildCommand { self } - /// Write build result metadata to file + /// Writes build result metadata to a file. /// /// # Examples /// @@ -916,7 +916,7 @@ impl BuildCommand { self } - /// Do not cache specified stage + /// Disables caching for specified stages. /// /// # Examples /// @@ -932,7 +932,7 @@ impl BuildCommand { self } - /// Set type of progress output + /// Sets the type of progress output. /// /// # Examples /// @@ -948,7 +948,7 @@ impl BuildCommand { self } - /// Set provenance attestation (shorthand for "--attest=type=provenance") + /// Sets provenance attestation (shorthand for "--attest=type=provenance"). /// /// # Examples /// @@ -964,7 +964,7 @@ impl BuildCommand { self } - /// Enable push mode (shorthand for "--output=type=registry") + /// Enables push mode (shorthand for "--output=type=registry"). /// /// # Examples /// @@ -980,7 +980,7 @@ impl BuildCommand { self } - /// Set SBOM attestation (shorthand for "--attest=type=sbom") + /// Sets SBOM attestation (shorthand for "--attest=type=sbom"). /// /// # Examples /// @@ -996,7 +996,7 @@ impl BuildCommand { self } - /// Add secret to expose to the build + /// Adds a secret to expose to the build. /// /// # Examples /// @@ -1012,7 +1012,7 @@ impl BuildCommand { self } - /// Add SSH agent socket or keys to expose + /// Adds an SSH agent socket or keys to expose. /// /// # Examples /// @@ -1028,15 +1028,15 @@ impl BuildCommand { self } - /// Get a reference to the command executor + /// Gets a reference to the command executor. #[must_use] - pub fn get_executor(&self) -> &CommandExecutor { + pub fn executor(&self) -> &CommandExecutor { &self.executor } - /// Get a mutable reference to the command executor + /// Gets a mutable reference to the command executor. #[must_use] - pub fn get_executor_mut(&mut self) -> &mut CommandExecutor { + pub fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } } @@ -1296,6 +1296,10 @@ impl BuildCommand { impl DockerCommand for BuildCommand { type Output = BuildOutput; + fn command_name() -> &'static str { + "build" + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -1305,7 +1309,7 @@ impl DockerCommand for BuildCommand { } fn build_command_args(&self) -> Vec { - let mut args = vec!["build".to_string()]; + let mut args = Vec::new(); self.add_basic_args(&mut args); self.add_resource_args(&mut args); diff --git a/src/command/builder/build.rs b/src/command/builder/build.rs index 483ce56e..6e7e205e 100644 --- a/src/command/builder/build.rs +++ b/src/command/builder/build.rs @@ -1,18 +1,19 @@ -//! Docker builder build command +//! Docker builder build command. //! -//! Alternative interface to start a build (similar to `docker build`) +//! Alternative interface to start a build (similar to `docker build`). use crate::command::build::{BuildCommand, BuildOutput}; use crate::command::{CommandExecutor, DockerCommand}; use crate::error::Result; use async_trait::async_trait; -/// `docker builder build` command - alternative interface to docker build +/// `docker builder build` command - alternative interface to docker build. /// /// This is essentially the same as `docker build` but accessed through /// the builder subcommand interface. /// /// # Example +/// /// ```no_run /// use docker_wrapper::command::builder::BuilderBuildCommand; /// use docker_wrapper::DockerCommand; From 7f9b18975f6715155712021a0a0b706b6c0e326e Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 12:31:37 +0800 Subject: [PATCH 5/9] format: `build.rs` --- src/command/build.rs | 115 +++++++++++++++++------------------ src/command/builder/build.rs | 15 ++--- 2 files changed, 62 insertions(+), 68 deletions(-) diff --git a/src/command/build.rs b/src/command/build.rs index 4f2de7b3..48eaf4df 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -16,97 +16,97 @@ use tokio::sync::mpsc; #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub struct BuildCommand { - /// Build context (path, URL, or stdin) + /// Build context (path, URL, or stdin). context: String, - /// Command executor for extensibility + /// Command executor for extensibility. pub executor: CommandExecutor, - /// Custom host-to-IP mappings + /// Custom host-to-IP mappings. add_hosts: Vec, - /// Build-time variables + /// Build-time variables. build_args: HashMap, - /// Images to consider as cache sources + /// Images to consider as cache sources. cache_from: Vec, - /// Parent cgroup for RUN instructions + /// Parent cgroup for RUN instructions. cgroup_parent: Option, - /// Compress the build context using gzip + /// Compress the build context using gzip. compress: bool, - /// CPU limits + /// CPU limits. cpu_period: Option, cpu_quota: Option, cpu_shares: Option, cpuset_cpus: Option, cpuset_mems: Option, - /// Skip image verification + /// Skip image verification. disable_content_trust: bool, - /// Name of the Dockerfile + /// Name of the Dockerfile. file: Option, - /// Always remove intermediate containers + /// Always remove intermediate containers. force_rm: bool, - /// Write the image ID to file + /// Writes the image ID to file. iidfile: Option, - /// Container isolation technology + /// Container isolation technology. isolation: Option, - /// Set metadata for an image + /// Sets metadata for an image. labels: HashMap, - /// Memory limit + /// Memory limit. memory: Option, - /// Memory + swap limit + /// Memory + swap limit. memory_swap: Option, - /// Networking mode for RUN instructions + /// Networking mode for RUN instructions. network: Option, - /// Do not use cache when building + /// Do not use cache when building. no_cache: bool, - /// Set platform for multi-platform builds + /// Set platform for multi-platform builds. platform: Option, - /// Always attempt to pull newer base images + /// Always attempt to pull newer base images. pull: bool, - /// Suppress build output and print image ID on success + /// Suppresses build output and print image ID on success. quiet: bool, - /// Remove intermediate containers after successful build + /// Removes intermediate containers after successful build. rm: bool, - /// Security options + /// Security options. security_opts: Vec, - /// Size of /dev/shm + /// Size of `/dev/shm`. shm_size: Option, - /// Name and tag for the image + /// Name and tag for the image. tags: Vec, - /// Target build stage + /// Target build stage. target: Option, - /// Ulimit options + /// Ulimit options. ulimits: Vec, - /// Extra privileged entitlements + /// Extra privileged entitlements. allow: Vec, - /// Annotations to add to the image + /// Annotations to add to the image. annotations: Vec, - /// Attestation parameters + /// Attestation parameters. attestations: Vec, - /// Additional build contexts + /// Additional build contexts. build_contexts: Vec, - /// Override the configured builder + /// Overrides the configured builder. builder: Option, - /// Cache export destinations + /// Cache export destinations. cache_to: Vec, - /// Method for evaluating build + /// Method for evaluating build. call: Option, - /// Shorthand for "--call=check" + /// Shorthand for `--call=check`. check: bool, - /// Shorthand for "--output=type=docker" + /// Shorthand for `--output=type=docker`. load: bool, - /// Write build result metadata to file + /// Writes build result metadata to file. metadata_file: Option, - /// Do not cache specified stages + /// Do not cache specified stages. no_cache_filter: Vec, - /// Type of progress output + /// Type of progress output. progress: Option, - /// Shorthand for "--attest=type=provenance" + /// Shorthand for `--attest=type=provenance`. provenance: Option, - /// Shorthand for "--output=type=registry" + /// Shorthand for `--output=type=registry`. push: bool, - /// Shorthand for "--attest=type=sbom" + /// Shorthand for `--attest=type=sbom`. sbom: Option, - /// Secrets to expose to the build + /// Secrets to expose to the build. secrets: Vec, - /// SSH agent socket or keys to expose + /// SSH agent socket or keys to expose. ssh: Vec, } @@ -1049,19 +1049,19 @@ impl Default for BuildCommand { impl BuildCommand { fn add_basic_args(&self, args: &mut Vec) { - // Add host mappings + // add host mappings for host in &self.add_hosts { args.push("--add-host".to_string()); args.push(host.clone()); } - // Add build arguments + // add build arguments for (key, value) in &self.build_args { args.push("--build-arg".to_string()); args.push(format!("{key}={value}")); } - // Add cache sources + // add cache sources for cache in &self.cache_from { args.push("--cache-from".to_string()); args.push(cache.clone()); @@ -1084,7 +1084,7 @@ impl BuildCommand { args.push("--quiet".to_string()); } - // Add tags + // add tags for tag in &self.tags { args.push("--tag".to_string()); args.push(tag.clone()); @@ -1186,13 +1186,13 @@ impl BuildCommand { args.push("--rm=false".to_string()); } - // Add security options + // add security options for opt in &self.security_opts { args.push("--security-opt".to_string()); args.push(opt.clone()); } - // Add ulimits + // add ulimits for limit in &self.ulimits { args.push("--ulimit".to_string()); args.push(limit.clone()); @@ -1200,7 +1200,7 @@ impl BuildCommand { } fn add_metadata_args(&self, args: &mut Vec) { - // Add labels + // add labels for (key, value) in &self.labels { args.push("--label".to_string()); args.push(format!("{key}={value}")); @@ -1315,10 +1315,10 @@ impl DockerCommand for BuildCommand { self.add_resource_args(&mut args); self.add_advanced_args(&mut args); - // Add any additional raw arguments + // add any additional raw arguments args.extend(self.executor.raw_args.clone()); - // Add build context (must be last) + // add build context (must be last) args.push(self.context.clone()); args @@ -1328,9 +1328,9 @@ impl DockerCommand for BuildCommand { let args = self.build_command_args(); let output = self.execute_command(args).await?; - // Extract image ID from output + // extract image ID from output let image_id = if self.quiet { - // In quiet mode, the output should be just the image ID + // in quiet mode, the output should be just the image ID Some(output.stdout.trim().to_string()) } else { let combined = if output.stderr.is_empty() { @@ -1352,7 +1352,6 @@ impl DockerCommand for BuildCommand { } } -// Streaming support for BuildCommand #[async_trait] impl StreamableCommand for BuildCommand { async fn stream(&self, handler: F) -> Result @@ -1380,7 +1379,7 @@ impl StreamableCommand for BuildCommand { } impl BuildCommand { - /// Run the build command with streaming output + /// Runs the build command with streaming output. /// /// # Examples /// @@ -1399,7 +1398,7 @@ impl BuildCommand { /// /// # Errors /// - /// Returns an error if the build fails or encounters an I/O error + /// Returns an error if the build fails or encounters an I/O error. pub async fn stream(&self, handler: F) -> Result where F: FnMut(OutputLine) + Send + 'static, diff --git a/src/command/builder/build.rs b/src/command/builder/build.rs index 6e7e205e..f94601dd 100644 --- a/src/command/builder/build.rs +++ b/src/command/builder/build.rs @@ -142,6 +142,10 @@ impl BuilderBuildCommand { impl DockerCommand for BuilderBuildCommand { type Output = BuildOutput; + fn command_name() -> &str { + "builder build" + } + fn executor(&self) -> &CommandExecutor { &self.inner.executor } @@ -151,16 +155,7 @@ impl DockerCommand for BuilderBuildCommand { } fn build_command_args(&self) -> Vec { - // Get the args from the inner build command - let mut inner_args = self.inner.build_command_args(); - - // Replace "build" with "builder build" - if !inner_args.is_empty() && inner_args[0] == "build" { - inner_args[0] = "builder".to_string(); - inner_args.insert(1, "build".to_string()); - } - - inner_args + self.inner.build_command_args(); } async fn execute(&self) -> Result { From aad5f40b85c384fcbd02edf981758812ddbe5a4b Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 12:38:38 +0800 Subject: [PATCH 6/9] feat: awkward builders --- src/command/attach.rs | 12 ++--- src/command/builder/build.rs | 45 +++++++++---------- src/command/builder/mod.rs | 2 +- src/command/builder/prune.rs | 70 ++++++++++++++++-------------- src/command/commit.rs | 14 +++--- src/command/context/create.rs | 2 +- src/command/context/inspect.rs | 2 +- src/command/context/ls.rs | 2 +- src/command/context/rm.rs | 2 +- src/command/context/update.rs | 2 +- src/command/context/use_context.rs | 2 +- src/command/cp.rs | 8 ++-- src/command/create.rs | 16 +++---- src/command/diff.rs | 6 +-- src/command/events.rs | 12 ++--- src/command/export.rs | 8 ++-- src/command/history.rs | 14 +++--- src/command/import.rs | 14 +++--- src/command/inspect.rs | 10 ++--- src/command/kill.rs | 2 +- src/command/load.rs | 10 ++--- src/command/pause.rs | 8 ++-- src/command/port.rs | 8 ++-- src/command/rename.rs | 6 +-- src/command/rm.rs | 2 +- src/command/rmi.rs | 10 ++--- src/command/run.rs | 4 +- src/command/save.rs | 10 ++--- src/command/stats.rs | 14 +++--- src/command/tag.rs | 6 +-- src/command/top.rs | 8 ++-- src/command/unpause.rs | 8 ++-- src/command/update.rs | 34 +++++++-------- src/command/wait.rs | 8 ++-- src/template/redis/enterprise.rs | 2 +- 35 files changed, 195 insertions(+), 188 deletions(-) diff --git a/src/command/attach.rs b/src/command/attach.rs index 5d3c260d..bf0adf02 100644 --- a/src/command/attach.rs +++ b/src/command/attach.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Attach local standard input, output, and error streams to a running container. /// -/// # Example +/// # Examples /// /// ```no_run /// use docker_wrapper::AttachCommand; @@ -46,7 +46,7 @@ pub struct AttachCommand { impl AttachCommand { /// Create a new attach command /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::AttachCommand; @@ -66,7 +66,7 @@ impl AttachCommand { /// Override the key sequence for detaching a container /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::AttachCommand; @@ -82,7 +82,7 @@ impl AttachCommand { /// Do not attach STDIN /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::AttachCommand; @@ -98,7 +98,7 @@ impl AttachCommand { /// Do not proxy signals /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::AttachCommand; @@ -120,7 +120,7 @@ impl AttachCommand { /// - The container doesn't exist /// - The container is not running /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::AttachCommand; diff --git a/src/command/builder/build.rs b/src/command/builder/build.rs index f94601dd..4bdb1cc4 100644 --- a/src/command/builder/build.rs +++ b/src/command/builder/build.rs @@ -12,7 +12,7 @@ use async_trait::async_trait; /// This is essentially the same as `docker build` but accessed through /// the builder subcommand interface. /// -/// # Example +/// # Examples /// /// ```no_run /// use docker_wrapper::command::builder::BuilderBuildCommand; @@ -33,64 +33,65 @@ use async_trait::async_trait; /// ``` #[derive(Debug, Clone)] pub struct BuilderBuildCommand { - /// Underlying build command + /// Underlying build command. inner: BuildCommand, } impl BuilderBuildCommand { - /// Create a new builder build command + /// Creates a new builder build command. /// /// # Arguments - /// * `context` - Build context path (e.g., ".", "/path/to/dir") + /// + /// * `context` - Build context path (e.g., `.`, `/path/to/dir`). pub fn new(context: impl Into) -> Self { Self { inner: BuildCommand::new(context), } } - /// Set the Dockerfile to use + /// Sets the Dockerfile to use. #[must_use] pub fn dockerfile(mut self, path: impl Into) -> Self { self.inner = self.inner.file(path.into()); self } - /// Tag the image + /// Tags the image. #[must_use] pub fn tag(mut self, tag: impl Into) -> Self { self.inner = self.inner.tag(tag); self } - /// Do not use cache when building + /// Do not use cache when building. #[must_use] pub fn no_cache(mut self) -> Self { self.inner = self.inner.no_cache(); self } - /// Set build-time variables + /// Sets build-time variables. #[must_use] pub fn build_arg(mut self, key: impl Into, value: impl Into) -> Self { self.inner = self.inner.build_arg(key, value); self } - /// Set target build stage + /// Sets target build stage. #[must_use] pub fn target(mut self, target: impl Into) -> Self { self.inner = self.inner.target(target); self } - /// Set platform for multi-platform builds + /// Sets platform for multi-platform builds. #[must_use] pub fn platform(mut self, platform: impl Into) -> Self { self.inner = self.inner.platform(platform); self } - /// Enable `BuildKit` backend + /// Enables `BuildKit` backend. #[must_use] pub fn buildkit(mut self) -> Self { // This would normally set DOCKER_BUILDKIT=1 environment variable @@ -102,35 +103,35 @@ impl BuilderBuildCommand { self } - /// Enable quiet mode + /// Enables quiet mode. #[must_use] pub fn quiet(mut self) -> Self { self.inner = self.inner.quiet(); self } - /// Always remove intermediate containers + /// Always removes intermediate containers. #[must_use] pub fn force_rm(mut self) -> Self { self.inner = self.inner.force_rm(); self } - /// Remove intermediate containers after successful build (default) + /// Removes intermediate containers after successful build (default). #[must_use] pub fn rm(self) -> Self { // rm is the default behavior, this is a no-op self } - /// Do not remove intermediate containers after build + /// Does not remove intermediate containers after build. #[must_use] pub fn no_rm(mut self) -> Self { self.inner = self.inner.no_rm(); self } - /// Always attempt to pull newer version of base image + /// Always attempts to pull newer version of base image. #[must_use] pub fn pull(mut self) -> Self { self.inner = self.inner.pull(); @@ -142,7 +143,7 @@ impl BuilderBuildCommand { impl DockerCommand for BuilderBuildCommand { type Output = BuildOutput; - fn command_name() -> &str { + fn command_name() -> &'static str { "builder build" } @@ -155,15 +156,15 @@ impl DockerCommand for BuilderBuildCommand { } fn build_command_args(&self) -> Vec { - self.inner.build_command_args(); + self.inner.build_command_args() } async fn execute(&self) -> Result { - // The builder build command has the same output as regular build + // the builder build command has the same output as regular build let args = self.build_command_args(); let output = self.inner.executor.execute_command("docker", args).await?; - // Extract image ID from output + // extract image ID from output let image_id = extract_image_id(&output.stdout); Ok(BuildOutput { @@ -175,9 +176,9 @@ impl DockerCommand for BuilderBuildCommand { } } -/// Extract image ID from build output +/// Extracts image ID from build output. fn extract_image_id(stdout: &str) -> Option { - // Look for "Successfully built " or "writing image sha256:" + // look for "Successfully built " or "writing image sha256:" for line in stdout.lines().rev() { if line.contains("Successfully built") { return line.split_whitespace().last().map(String::from); diff --git a/src/command/builder/mod.rs b/src/command/builder/mod.rs index 18489fe4..05176e09 100644 --- a/src/command/builder/mod.rs +++ b/src/command/builder/mod.rs @@ -1,4 +1,4 @@ -//! Docker builder commands for build cache management +//! Docker builder commands for build cache management. //! //! This module provides support for Docker builder commands that manage //! the build subsystem and build cache. diff --git a/src/command/builder/prune.rs b/src/command/builder/prune.rs index 0bc6b16b..e43caf26 100644 --- a/src/command/builder/prune.rs +++ b/src/command/builder/prune.rs @@ -1,21 +1,22 @@ -//! Docker builder prune command +//! Docker builder prune command. //! -//! Remove build cache +//! Removes build cache. use crate::command::{CommandExecutor, DockerCommand}; use crate::error::Result; use async_trait::async_trait; use std::collections::HashMap; -/// `docker builder prune` command to remove build cache +/// `docker builder prune` command to remove build cache. +/// +/// # Examples /// -/// # Example /// ```no_run /// use docker_wrapper::command::builder::BuilderPruneCommand; /// use docker_wrapper::DockerCommand; /// /// # async fn example() -> Result<(), Box> { -/// // Remove all build cache +/// // Removes all build cache. /// let result = BuilderPruneCommand::new() /// .all() /// .force() @@ -28,37 +29,37 @@ use std::collections::HashMap; /// ``` #[derive(Debug, Clone)] pub struct BuilderPruneCommand { - /// Remove all unused build cache, not just dangling ones + /// Removes all unused build cache, not just dangling ones. all: bool, - /// Provide filter values + /// Provides filter values. filters: HashMap, - /// Do not prompt for confirmation + /// Do not prompt for confirmation. force: bool, - /// Amount of disk storage to keep for cache + /// Amount of disk storage to keep for cache. keep_storage: Option, - /// Command executor + /// Command executor. pub executor: CommandExecutor, } -/// Result of builder prune operation +/// Result of builder prune operation. #[derive(Debug)] pub struct BuilderPruneResult { - /// IDs of deleted build cache entries + /// IDs of deleted build cache entries. pub deleted_cache_ids: Vec, - /// Amount of disk space reclaimed in bytes + /// Amount of disk space reclaimed in bytes. pub space_reclaimed: Option, - /// Human-readable space reclaimed (e.g., "2.5GB") + /// Human-readable space reclaimed (e.g., "2.5GB"). pub space_reclaimed_str: Option, - /// Standard output from the command + /// Standard output from the command. pub stdout: String, - /// Standard error from the command + /// Standard error from the command. pub stderr: String, - /// Exit code + /// Exit code. pub exit_code: i32, } impl BuilderPruneCommand { - /// Create a new builder prune command + /// Creates a new builder prune command. #[must_use] pub fn new() -> Self { Self { @@ -70,34 +71,35 @@ impl BuilderPruneCommand { } } - /// Remove all unused build cache, not just dangling ones + /// Removes all unused build cache, not just dangling ones. #[must_use] pub fn all(mut self) -> Self { self.all = true; self } - /// Add a filter to the prune operation + /// Adds a filter to the prune operation. /// /// Common filters: - /// - `until=` - only remove cache created before given timestamp - /// - `until=24h` - only remove cache older than 24 hours + /// - `until=` - only remove cache created before given timestamp; + /// - `until=24h` - only remove cache older than 24 hours. #[must_use] pub fn filter(mut self, key: impl Into, value: impl Into) -> Self { self.filters.insert(key.into(), value.into()); self } - /// Do not prompt for confirmation + /// Do not prompt for confirmation. #[must_use] pub fn force(mut self) -> Self { self.force = true; self } - /// Amount of disk storage to keep for cache + /// Amount of disk storage to keep for cache. + /// + /// # Examples /// - /// # Example /// ```no_run /// # use docker_wrapper::command::builder::BuilderPruneCommand; /// # use docker_wrapper::DockerCommand; @@ -115,25 +117,25 @@ impl BuilderPruneCommand { self } - /// Parse the prune output to extract cache IDs and space reclaimed + /// Parses the prune output to extract cache IDs and space reclaimed. fn parse_output(output: &str) -> (Vec, Option, Option) { let mut cache_ids = Vec::new(); let mut space_reclaimed = None; let mut space_reclaimed_str = None; for line in output.lines() { - // Parse deleted cache entries (format: "Deleted: sha256:...") + // parse deleted cache entries (format: "Deleted: sha256:...") if line.starts_with("Deleted:") || line.starts_with("deleted:") { if let Some(id) = line.split_whitespace().nth(1) { cache_ids.push(id.to_string()); } } - // Parse total reclaimed space + // parse total reclaimed space if line.contains("Total reclaimed space:") || line.contains("total reclaimed space:") { space_reclaimed_str = line.split(':').nth(1).map(|s| s.trim().to_string()); - // Try to parse the bytes value + // try to parse the bytes value if let Some(size_str) = &space_reclaimed_str { space_reclaimed = parse_size(size_str); } @@ -154,6 +156,10 @@ impl Default for BuilderPruneCommand { impl DockerCommand for BuilderPruneCommand { type Output = BuilderPruneResult; + fn command_name() -> &'static str { + "builder prune" + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -163,7 +169,7 @@ impl DockerCommand for BuilderPruneCommand { } fn build_command_args(&self) -> Vec { - let mut args = vec!["builder".to_string(), "prune".to_string()]; + let mut args = Vec::new(); if self.all { args.push("--all".to_string()); @@ -207,14 +213,14 @@ impl DockerCommand for BuilderPruneCommand { } } -/// Parse a size string (e.g., "2.5GB", "100MB") into bytes +/// Parses a size string (e.g., "2.5GB", "100MB") into bytes. #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_precision_loss)] fn parse_size(size_str: &str) -> Option { let size_str = size_str.trim(); - // Try to extract number and unit + // try to extract number and unit let (num_str, unit) = if let Some(pos) = size_str.find(|c: char| c.is_alphabetic()) { (&size_str[..pos], &size_str[pos..]) } else { diff --git a/src/command/commit.rs b/src/command/commit.rs index fd269304..a98319a4 100644 --- a/src/command/commit.rs +++ b/src/command/commit.rs @@ -11,7 +11,7 @@ use async_trait::async_trait; /// /// Create a new image from a container's changes. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::CommitCommand; @@ -53,7 +53,7 @@ pub struct CommitCommand { impl CommitCommand { /// Create a new commit command /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::CommitCommand; @@ -76,7 +76,7 @@ impl CommitCommand { /// Set the repository name for the new image /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CommitCommand; @@ -92,7 +92,7 @@ impl CommitCommand { /// Set the tag for the new image /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CommitCommand; @@ -116,7 +116,7 @@ impl CommitCommand { /// Set the author /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CommitCommand; @@ -139,7 +139,7 @@ impl CommitCommand { /// Apply Dockerfile instruction to the created image /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CommitCommand; @@ -163,7 +163,7 @@ impl CommitCommand { /// - The container doesn't exist /// - The repository/tag format is invalid /// - /// # Example + /// # Exampless /// /// ```no_run /// use docker_wrapper::CommitCommand; diff --git a/src/command/context/create.rs b/src/command/context/create.rs index ceb5e5a2..d346c237 100644 --- a/src/command/context/create.rs +++ b/src/command/context/create.rs @@ -8,7 +8,7 @@ use async_trait::async_trait; /// /// Create a new Docker context. /// -/// # Example +/// # Examplesss /// /// ```no_run /// use docker_wrapper::{ContextCreateCommand, DockerCommand}; diff --git a/src/command/context/inspect.rs b/src/command/context/inspect.rs index e0392609..70fe0de6 100644 --- a/src/command/context/inspect.rs +++ b/src/command/context/inspect.rs @@ -9,7 +9,7 @@ use serde_json::Value; /// /// Display detailed information on one or more contexts. /// -/// # Example +/// # Examplesss /// /// ```no_run /// use docker_wrapper::{ContextInspectCommand, DockerCommand}; diff --git a/src/command/context/ls.rs b/src/command/context/ls.rs index 6a7944fc..49bdaf9f 100644 --- a/src/command/context/ls.rs +++ b/src/command/context/ls.rs @@ -33,7 +33,7 @@ pub struct ContextInfo { /// /// Lists all Docker contexts. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::{ContextLsCommand, DockerCommand}; diff --git a/src/command/context/rm.rs b/src/command/context/rm.rs index 9168f910..6ede9e16 100644 --- a/src/command/context/rm.rs +++ b/src/command/context/rm.rs @@ -8,7 +8,7 @@ use async_trait::async_trait; /// /// Remove one or more Docker contexts. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::{ContextRmCommand, DockerCommand}; diff --git a/src/command/context/update.rs b/src/command/context/update.rs index 9d8d315e..951461f4 100644 --- a/src/command/context/update.rs +++ b/src/command/context/update.rs @@ -8,7 +8,7 @@ use async_trait::async_trait; /// /// Update an existing Docker context. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::{ContextUpdateCommand, DockerCommand}; diff --git a/src/command/context/use_context.rs b/src/command/context/use_context.rs index 5ee81ec8..bf73def3 100644 --- a/src/command/context/use_context.rs +++ b/src/command/context/use_context.rs @@ -8,7 +8,7 @@ use async_trait::async_trait; /// /// Switch to a different Docker context. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::{ContextUseCommand, DockerCommand}; diff --git a/src/command/cp.rs b/src/command/cp.rs index 3eb0de44..0f5e9a4c 100644 --- a/src/command/cp.rs +++ b/src/command/cp.rs @@ -13,7 +13,7 @@ use std::path::Path; /// Copy files/folders between a container and the local filesystem. /// Use `-` as the source to read from stdin or as the destination to write to stdout. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::CpCommand; @@ -53,7 +53,7 @@ pub struct CpCommand { impl CpCommand { /// Create a cp command copying from container to host /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::CpCommand; @@ -77,7 +77,7 @@ impl CpCommand { /// Create a cp command copying from host to container /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CpCommand; @@ -114,7 +114,7 @@ impl CpCommand { /// Archive mode - preserve UIDs/GIDs and permissions /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CpCommand; diff --git a/src/command/create.rs b/src/command/create.rs index 93e30049..78524f19 100644 --- a/src/command/create.rs +++ b/src/command/create.rs @@ -12,7 +12,7 @@ use async_trait::async_trait; /// Create a new container without starting it. This is useful for preparing /// containers that will be started later. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::CreateCommand; @@ -73,7 +73,7 @@ pub struct CreateCommand { impl CreateCommand { /// Create a new create command /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::CreateCommand; @@ -107,7 +107,7 @@ impl CreateCommand { /// Set the container name /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CreateCommand; @@ -123,7 +123,7 @@ impl CreateCommand { /// Set the command to run in the container /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CreateCommand; @@ -139,7 +139,7 @@ impl CreateCommand { /// Add an environment variable /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CreateCommand; @@ -156,7 +156,7 @@ impl CreateCommand { /// Add a port mapping /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CreateCommand; @@ -228,7 +228,7 @@ impl CreateCommand { /// Add a volume mount /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::CreateCommand; @@ -279,7 +279,7 @@ impl CreateCommand { /// - The specified image doesn't exist /// - Invalid configuration options /// - /// # Example + /// # Exampless /// /// ```no_run /// use docker_wrapper::CreateCommand; diff --git a/src/command/diff.rs b/src/command/diff.rs index 4da543ee..5d8af43c 100644 --- a/src/command/diff.rs +++ b/src/command/diff.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Inspect changes to files or folders on a container's filesystem. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::DiffCommand; @@ -38,7 +38,7 @@ pub struct DiffCommand { impl DiffCommand { /// Create a new diff command /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::DiffCommand; @@ -60,7 +60,7 @@ impl DiffCommand { /// - The Docker daemon is not running /// - The container doesn't exist /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::DiffCommand; diff --git a/src/command/events.rs b/src/command/events.rs index 270b86e6..69184494 100644 --- a/src/command/events.rs +++ b/src/command/events.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; /// /// Get real-time events from the Docker daemon. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::EventsCommand; @@ -48,7 +48,7 @@ pub struct EventsCommand { impl EventsCommand { /// Create a new events command /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::EventsCommand; @@ -68,7 +68,7 @@ impl EventsCommand { /// Add a filter for events /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::EventsCommand; @@ -86,7 +86,7 @@ impl EventsCommand { /// Set output format /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::EventsCommand; @@ -102,7 +102,7 @@ impl EventsCommand { /// Show events created since this timestamp /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::EventsCommand; @@ -130,7 +130,7 @@ impl EventsCommand { /// - The Docker daemon is not running /// - Invalid filter or timestamp format /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::EventsCommand; diff --git a/src/command/export.rs b/src/command/export.rs index 4d038092..cce13b73 100644 --- a/src/command/export.rs +++ b/src/command/export.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Export a container's filesystem as a tar archive. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::ExportCommand; @@ -41,7 +41,7 @@ pub struct ExportCommand { impl ExportCommand { /// Create a new export command /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::ExportCommand; @@ -59,7 +59,7 @@ impl ExportCommand { /// Set output file for the export /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::ExportCommand; @@ -82,7 +82,7 @@ impl ExportCommand { /// - File I/O errors occur during export /// - Insufficient disk space /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::ExportCommand; diff --git a/src/command/history.rs b/src/command/history.rs index feea45c4..8200f7c8 100644 --- a/src/command/history.rs +++ b/src/command/history.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; /// /// Show the history of an image, including layer information. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::HistoryCommand; @@ -47,7 +47,7 @@ pub struct HistoryCommand { impl HistoryCommand { /// Create a new history command /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::HistoryCommand; @@ -68,7 +68,7 @@ impl HistoryCommand { /// Show human readable sizes /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::HistoryCommand; @@ -84,7 +84,7 @@ impl HistoryCommand { /// Don't truncate output /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::HistoryCommand; @@ -100,7 +100,7 @@ impl HistoryCommand { /// Show quiet output (only image IDs) /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::HistoryCommand; @@ -116,7 +116,7 @@ impl HistoryCommand { /// Format output using a Go template /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::HistoryCommand; @@ -137,7 +137,7 @@ impl HistoryCommand { /// - The Docker daemon is not running /// - The image doesn't exist /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::HistoryCommand; diff --git a/src/command/import.rs b/src/command/import.rs index 35bb2ac1..54e51b5a 100644 --- a/src/command/import.rs +++ b/src/command/import.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Import the contents from a tarball to create a filesystem image. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::ImportCommand; @@ -45,7 +45,7 @@ pub struct ImportCommand { impl ImportCommand { /// Create a new import command /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::ImportCommand; @@ -72,7 +72,7 @@ impl ImportCommand { /// Set repository name for the imported image /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::ImportCommand; @@ -88,7 +88,7 @@ impl ImportCommand { /// Set commit message for the imported image /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::ImportCommand; @@ -104,7 +104,7 @@ impl ImportCommand { /// Apply Dockerfile instruction while importing /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::ImportCommand; @@ -121,7 +121,7 @@ impl ImportCommand { /// Apply multiple Dockerfile instructions while importing /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::ImportCommand; @@ -153,7 +153,7 @@ impl ImportCommand { /// - Network issues (for URL sources) /// - Insufficient disk space /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::ImportCommand; diff --git a/src/command/inspect.rs b/src/command/inspect.rs index 0f532e9f..4adc47b6 100644 --- a/src/command/inspect.rs +++ b/src/command/inspect.rs @@ -10,7 +10,7 @@ use serde_json::Value; /// Docker inspect command builder /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::InspectCommand; @@ -44,7 +44,7 @@ pub struct InspectCommand { impl InspectCommand { /// Create a new inspect command for a single object /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::InspectCommand; @@ -64,7 +64,7 @@ impl InspectCommand { /// Create a new inspect command for multiple objects /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::InspectCommand; @@ -91,7 +91,7 @@ impl InspectCommand { /// Set custom format string (Go template) /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::InspectCommand; @@ -211,7 +211,7 @@ impl InspectOutput { /// # Errors /// Returns an error if the output is not valid JSON /// - /// # Example + /// # Examples /// /// ```no_run /// # use docker_wrapper::InspectCommand; diff --git a/src/command/kill.rs b/src/command/kill.rs index 3cba2c6c..80aa2f2f 100644 --- a/src/command/kill.rs +++ b/src/command/kill.rs @@ -2,7 +2,7 @@ //! //! This module provides the `docker kill` command for sending signals to running containers. //! -//! # Example +//! # Examples //! //! ```rust,no_run //! use docker_wrapper::{DockerCommand, KillCommand, RunCommand}; diff --git a/src/command/load.rs b/src/command/load.rs index 66352ac3..04e0eee8 100644 --- a/src/command/load.rs +++ b/src/command/load.rs @@ -11,7 +11,7 @@ use std::path::Path; /// /// Load an image from a tar archive or STDIN. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::LoadCommand; @@ -41,7 +41,7 @@ pub struct LoadCommand { impl LoadCommand { /// Create a new load command /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::LoadCommand; @@ -59,7 +59,7 @@ impl LoadCommand { /// Set input file path /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::LoadCommand; @@ -76,7 +76,7 @@ impl LoadCommand { /// Suppress progress output during load /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::LoadCommand; @@ -97,7 +97,7 @@ impl LoadCommand { /// - The input file doesn't exist or is not readable /// - The tar archive is corrupted or invalid /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::LoadCommand; diff --git a/src/command/pause.rs b/src/command/pause.rs index 92c07fba..605f4e34 100644 --- a/src/command/pause.rs +++ b/src/command/pause.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Pause all processes within one or more containers. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::PauseCommand; @@ -39,7 +39,7 @@ pub struct PauseCommand { impl PauseCommand { /// Create a new pause command for a single container /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::PauseCommand; @@ -56,7 +56,7 @@ impl PauseCommand { /// Create a new pause command for multiple containers /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::PauseCommand; @@ -86,7 +86,7 @@ impl PauseCommand { /// - Any of the specified containers don't exist /// - Any container is not running /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::PauseCommand; diff --git a/src/command/port.rs b/src/command/port.rs index bc3c6988..d80ed544 100644 --- a/src/command/port.rs +++ b/src/command/port.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// List port mappings or a specific mapping for a container. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::PortCommand; @@ -42,7 +42,7 @@ pub struct PortCommand { impl PortCommand { /// Create a new port command /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::PortCommand; @@ -60,7 +60,7 @@ impl PortCommand { /// Query specific port mapping /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::PortCommand; @@ -81,7 +81,7 @@ impl PortCommand { /// - The Docker daemon is not running /// - The container doesn't exist /// - /// # Example + /// # Exampless /// /// ```no_run /// use docker_wrapper::PortCommand; diff --git a/src/command/rename.rs b/src/command/rename.rs index ddafb876..c3971a5b 100644 --- a/src/command/rename.rs +++ b/src/command/rename.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Rename a container. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::RenameCommand; @@ -40,7 +40,7 @@ pub struct RenameCommand { impl RenameCommand { /// Create a new rename command /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::RenameCommand; @@ -65,7 +65,7 @@ impl RenameCommand { /// - The new name is already in use /// - The container is running (some Docker versions) /// - /// # Example + /// # Exampless /// /// ```no_run /// use docker_wrapper::RenameCommand; diff --git a/src/command/rm.rs b/src/command/rm.rs index 70f4b3bb..deb44a93 100644 --- a/src/command/rm.rs +++ b/src/command/rm.rs @@ -2,7 +2,7 @@ //! //! This module provides the `docker rm` command for removing stopped containers. //! -//! # Example +//! # Examples //! //! ```rust,no_run //! use docker_wrapper::{DockerCommand, RmCommand, RunCommand, StopCommand}; diff --git a/src/command/rmi.rs b/src/command/rmi.rs index eb695688..963e0a11 100644 --- a/src/command/rmi.rs +++ b/src/command/rmi.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Remove one or more images. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::RmiCommand; @@ -44,7 +44,7 @@ pub struct RmiCommand { impl RmiCommand { /// Create a new rmi command for a single image /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::RmiCommand; @@ -63,7 +63,7 @@ impl RmiCommand { /// Create a new rmi command for multiple images /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::RmiCommand; @@ -89,7 +89,7 @@ impl RmiCommand { /// Force removal of the images /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::RmiCommand; @@ -118,7 +118,7 @@ impl RmiCommand { /// - Any of the specified images don't exist /// - Images are in use by containers (unless force is used) /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::RmiCommand; diff --git a/src/command/run.rs b/src/command/run.rs index 14532c9c..84897625 100644 --- a/src/command/run.rs +++ b/src/command/run.rs @@ -322,7 +322,7 @@ impl ContainerId { /// This queries Docker for the actual mapped ports of the running container. /// Useful when using dynamic port allocation (e.g., `-p 6379` without specifying host port). /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::{DockerCommand, RunCommand}; @@ -359,7 +359,7 @@ impl ContainerId { /// Get a specific port mapping for this container /// - /// # Example + /// # Exampless /// /// ```no_run /// use docker_wrapper::{DockerCommand, RunCommand}; diff --git a/src/command/save.rs b/src/command/save.rs index 3b7d1ff1..e93b3dba 100644 --- a/src/command/save.rs +++ b/src/command/save.rs @@ -11,7 +11,7 @@ use std::path::Path; /// /// Save one or more images to a tar archive (streamed to STDOUT by default). /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::SaveCommand; @@ -45,7 +45,7 @@ pub struct SaveCommand { impl SaveCommand { /// Create a new save command for a single image /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::SaveCommand; @@ -63,7 +63,7 @@ impl SaveCommand { /// Create a new save command for multiple images /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::SaveCommand; @@ -88,7 +88,7 @@ impl SaveCommand { /// Set output file path /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::SaveCommand; @@ -111,7 +111,7 @@ impl SaveCommand { /// - Any of the specified images don't exist /// - Cannot write to the output file /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::SaveCommand; diff --git a/src/command/stats.rs b/src/command/stats.rs index f3a2aa91..deea56b4 100644 --- a/src/command/stats.rs +++ b/src/command/stats.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; /// /// Display a live stream of container(s) resource usage statistics. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::StatsCommand; @@ -59,7 +59,7 @@ pub struct StatsCommand { impl StatsCommand { /// Create a new stats command /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::StatsCommand; @@ -80,7 +80,7 @@ impl StatsCommand { /// Add a container to monitor /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::StatsCommand; @@ -105,7 +105,7 @@ impl StatsCommand { /// Show stats for all containers (default shows only running) /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::StatsCommand; @@ -120,7 +120,7 @@ impl StatsCommand { /// Set output format /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::StatsCommand; @@ -139,7 +139,7 @@ impl StatsCommand { /// Disable streaming stats and only pull the first result /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::StatsCommand; @@ -167,7 +167,7 @@ impl StatsCommand { /// - Any specified container doesn't exist /// - No containers are running (when no specific containers are specified) /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::StatsCommand; diff --git a/src/command/tag.rs b/src/command/tag.rs index 03729988..46fa1e3b 100644 --- a/src/command/tag.rs +++ b/src/command/tag.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Create a tag `TARGET_IMAGE` that refers to `SOURCE_IMAGE`. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::TagCommand; @@ -41,7 +41,7 @@ pub struct TagCommand { impl TagCommand { /// Create a new tag command /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::TagCommand; @@ -69,7 +69,7 @@ impl TagCommand { /// - The source image doesn't exist /// - The target image name is invalid /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::TagCommand; diff --git a/src/command/top.rs b/src/command/top.rs index 6055d277..e79583c7 100644 --- a/src/command/top.rs +++ b/src/command/top.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Display the running processes of a container. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::TopCommand; @@ -42,7 +42,7 @@ pub struct TopCommand { impl TopCommand { /// Create a new top command /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::TopCommand; @@ -60,7 +60,7 @@ impl TopCommand { /// Set ps command options /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::TopCommand; @@ -87,7 +87,7 @@ impl TopCommand { /// - The container doesn't exist /// - The container is not running /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::TopCommand; diff --git a/src/command/unpause.rs b/src/command/unpause.rs index c2ecdcef..316627f9 100644 --- a/src/command/unpause.rs +++ b/src/command/unpause.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Unpause all processes within one or more containers. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::UnpauseCommand; @@ -39,7 +39,7 @@ pub struct UnpauseCommand { impl UnpauseCommand { /// Create a new unpause command for a single container /// - /// # Example + /// # Examplesss /// /// ``` /// use docker_wrapper::UnpauseCommand; @@ -56,7 +56,7 @@ impl UnpauseCommand { /// Create a new unpause command for multiple containers /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::UnpauseCommand; @@ -86,7 +86,7 @@ impl UnpauseCommand { /// - Any of the specified containers don't exist /// - Any container is not paused /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::UnpauseCommand; diff --git a/src/command/update.rs b/src/command/update.rs index 720ba1ff..2f22f54b 100644 --- a/src/command/update.rs +++ b/src/command/update.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Update configuration of one or more containers. /// -/// # Example +/// # Examplesss /// /// ```no_run /// use docker_wrapper::UpdateCommand; @@ -65,7 +65,7 @@ pub struct UpdateCommand { impl UpdateCommand { /// Create a new update command for a single container /// - /// # Example + /// # Examplessssss /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -95,7 +95,7 @@ impl UpdateCommand { /// Create a new update command for multiple containers /// - /// # Example + /// # Exampless /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -132,7 +132,7 @@ impl UpdateCommand { /// Set memory limit /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -148,7 +148,7 @@ impl UpdateCommand { /// Set memory reservation (soft limit) /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -164,7 +164,7 @@ impl UpdateCommand { /// Set memory swap limit /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -180,7 +180,7 @@ impl UpdateCommand { /// Set CPU shares (relative weight) /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -196,7 +196,7 @@ impl UpdateCommand { /// Set CPU period /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -212,7 +212,7 @@ impl UpdateCommand { /// Set CPU quota /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -228,7 +228,7 @@ impl UpdateCommand { /// Set number of CPUs /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -244,7 +244,7 @@ impl UpdateCommand { /// Set CPU set /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -260,7 +260,7 @@ impl UpdateCommand { /// Set memory nodes /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -276,7 +276,7 @@ impl UpdateCommand { /// Set block IO weight /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -292,7 +292,7 @@ impl UpdateCommand { /// Set kernel memory limit /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -308,7 +308,7 @@ impl UpdateCommand { /// Set restart policy /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -324,7 +324,7 @@ impl UpdateCommand { /// Set PID limit /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::UpdateCommand; @@ -346,7 +346,7 @@ impl UpdateCommand { /// - Any of the specified containers don't exist /// - Invalid resource limits are specified /// - /// # Example + /// # Examplesss /// /// ```no_run /// use docker_wrapper::UpdateCommand; diff --git a/src/command/wait.rs b/src/command/wait.rs index 9a4e4998..da0c9521 100644 --- a/src/command/wait.rs +++ b/src/command/wait.rs @@ -10,7 +10,7 @@ use async_trait::async_trait; /// /// Block until one or more containers stop, then print their exit codes. /// -/// # Example +/// # Exampless /// /// ```no_run /// use docker_wrapper::WaitCommand; @@ -41,7 +41,7 @@ pub struct WaitCommand { impl WaitCommand { /// Create a new wait command for a single container /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::WaitCommand; @@ -58,7 +58,7 @@ impl WaitCommand { /// Create a new wait command for multiple containers /// - /// # Example + /// # Examples /// /// ``` /// use docker_wrapper::WaitCommand; @@ -87,7 +87,7 @@ impl WaitCommand { /// - The Docker daemon is not running /// - Any of the specified containers don't exist /// - /// # Example + /// # Examples /// /// ```no_run /// use docker_wrapper::WaitCommand; diff --git a/src/template/redis/enterprise.rs b/src/template/redis/enterprise.rs index a24ff93f..6153a0ec 100644 --- a/src/template/redis/enterprise.rs +++ b/src/template/redis/enterprise.rs @@ -140,7 +140,7 @@ impl RedisEnterpriseTemplate { /// Use a custom Redis Enterprise image and tag /// - /// # Example + /// # Examples /// ``` /// # use docker_wrapper::RedisEnterpriseTemplate; /// let template = RedisEnterpriseTemplate::new("my-redis") From 053dacc6531adfba71e2aaeeb6a0600bc4785d3f Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 12:53:14 +0800 Subject: [PATCH 7/9] feat: organize compose commands --- src/command.rs | 248 +------- src/command/compose.rs | 30 + .../{compose_attach.rs => compose/attach.rs} | 0 .../{compose_build.rs => compose/build.rs} | 0 .../{compose_config.rs => compose/config.rs} | 0 .../convert.rs} | 0 src/command/{compose_cp.rs => compose/cp.rs} | 0 .../{compose_create.rs => compose/create.rs} | 0 .../{compose_down.rs => compose/down.rs} | 0 .../{compose_events.rs => compose/events.rs} | 0 .../{compose_exec.rs => compose/exec.rs} | 0 .../{compose_images.rs => compose/images.rs} | 0 .../{compose_kill.rs => compose/kill.rs} | 0 .../{compose_logs.rs => compose/logs.rs} | 0 src/command/{compose_ls.rs => compose/ls.rs} | 0 .../{compose_pause.rs => compose/pause.rs} | 0 .../{compose_port.rs => compose/port.rs} | 0 src/command/{compose_ps.rs => compose/ps.rs} | 0 .../{compose_push.rs => compose/push.rs} | 0 .../restart.rs} | 0 src/command/{compose_rm.rs => compose/rm.rs} | 0 .../{compose_run.rs => compose/run.rs} | 0 .../{compose_scale.rs => compose/scale.rs} | 0 .../{compose_start.rs => compose/start.rs} | 0 .../{compose_stop.rs => compose/stop.rs} | 0 .../{compose_top.rs => compose/top.rs} | 0 .../unpause.rs} | 0 src/command/{compose_up.rs => compose/up.rs} | 0 .../version.rs} | 0 .../{compose_wait.rs => compose/wait.rs} | 0 .../{compose_watch.rs => compose/watch.rs} | 0 src/compose.rs | 589 +++--------------- src/lib.rs | 39 +- 33 files changed, 157 insertions(+), 749 deletions(-) create mode 100644 src/command/compose.rs rename src/command/{compose_attach.rs => compose/attach.rs} (100%) rename src/command/{compose_build.rs => compose/build.rs} (100%) rename src/command/{compose_config.rs => compose/config.rs} (100%) rename src/command/{compose_convert.rs => compose/convert.rs} (100%) rename src/command/{compose_cp.rs => compose/cp.rs} (100%) rename src/command/{compose_create.rs => compose/create.rs} (100%) rename src/command/{compose_down.rs => compose/down.rs} (100%) rename src/command/{compose_events.rs => compose/events.rs} (100%) rename src/command/{compose_exec.rs => compose/exec.rs} (100%) rename src/command/{compose_images.rs => compose/images.rs} (100%) rename src/command/{compose_kill.rs => compose/kill.rs} (100%) rename src/command/{compose_logs.rs => compose/logs.rs} (100%) rename src/command/{compose_ls.rs => compose/ls.rs} (100%) rename src/command/{compose_pause.rs => compose/pause.rs} (100%) rename src/command/{compose_port.rs => compose/port.rs} (100%) rename src/command/{compose_ps.rs => compose/ps.rs} (100%) rename src/command/{compose_push.rs => compose/push.rs} (100%) rename src/command/{compose_restart.rs => compose/restart.rs} (100%) rename src/command/{compose_rm.rs => compose/rm.rs} (100%) rename src/command/{compose_run.rs => compose/run.rs} (100%) rename src/command/{compose_scale.rs => compose/scale.rs} (100%) rename src/command/{compose_start.rs => compose/start.rs} (100%) rename src/command/{compose_stop.rs => compose/stop.rs} (100%) rename src/command/{compose_top.rs => compose/top.rs} (100%) rename src/command/{compose_unpause.rs => compose/unpause.rs} (100%) rename src/command/{compose_up.rs => compose/up.rs} (100%) rename src/command/{compose_version.rs => compose/version.rs} (100%) rename src/command/{compose_wait.rs => compose/wait.rs} (100%) rename src/command/{compose_watch.rs => compose/watch.rs} (100%) diff --git a/src/command.rs b/src/command.rs index 049b3b6f..ce25f130 100644 --- a/src/command.rs +++ b/src/command.rs @@ -9,33 +9,15 @@ use crate::platform::PlatformInfo; use async_trait::async_trait; use std::collections::HashMap; use std::ffi::OsStr; -use std::path::PathBuf; use std::process::Stdio; use tokio::process::Command as TokioCommand; -// Re-export all command modules +// re-exports all command modules pub mod attach; pub mod bake; pub mod build; pub mod builder; pub mod commit; -pub mod compose_attach; -pub mod compose_build; -pub mod compose_create; -pub mod compose_down; -pub mod compose_exec; -pub mod compose_kill; -pub mod compose_logs; -pub mod compose_ls; -pub mod compose_pause; -pub mod compose_ps; -pub mod compose_restart; -pub mod compose_rm; -pub mod compose_run; -pub mod compose_start; -pub mod compose_stop; -pub mod compose_unpause; -pub mod compose_up; pub mod container_prune; pub mod context; pub mod cp; @@ -81,13 +63,16 @@ pub mod version; pub mod volume; pub mod wait; +#[cfg(feature = "compose")] +pub mod compose; + /// Unified trait for all Docker commands (both regular and compose). #[async_trait] pub trait DockerCommand { /// The output type this command produces. type Output; - /// Gets the command name (e.g., "compose", "pull") + /// Gets the command name (e.g., `compose`, `build`, `run`). fn command_name() -> &'static str; /// Gets the command executor for extensibility. @@ -138,31 +123,6 @@ pub trait DockerCommand { } } -/// Base configuration for all compose commands. -#[derive(Debug, Clone, Default)] -pub struct ComposeConfig { - /// Compose file paths (-f, --file). - pub files: Vec, - /// Project name (-p, --project-name). - pub project_name: Option, - /// Project directory (--project-directory). - pub project_directory: Option, - /// Profiles to enable (--profile). - pub profiles: Vec, - /// Environment file (--env-file). - pub env_file: Option, - /// Run in compatibility mode. - pub compatibility: bool, - /// Execute in dry run mode. - pub dry_run: bool, - /// Progress output type. - pub progress: Option, - /// ANSI control characters. - pub ansi: Option, - /// Max parallelism (-1 for unlimited). - pub parallel: Option, -} - /// Progress output type for compose commands. #[derive(Debug, Clone, Copy)] pub enum ProgressType { @@ -211,204 +171,6 @@ impl std::fmt::Display for AnsiMode { } } -impl ComposeConfig { - /// Creates a new compose configuration. - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Adds a compose file. - #[must_use] - pub fn file(mut self, path: impl Into) -> Self { - self.files.push(path.into()); - self - } - - /// Sets the project name. - #[must_use] - pub fn project_name(mut self, name: impl Into) -> Self { - self.project_name = Some(name.into()); - self - } - - /// Sets the project directory. - #[must_use] - pub fn project_directory(mut self, dir: impl Into) -> Self { - self.project_directory = Some(dir.into()); - self - } - - /// Adds a profile. - #[must_use] - pub fn profile(mut self, profile: impl Into) -> Self { - self.profiles.push(profile.into()); - self - } - - /// Sets environment file. - #[must_use] - pub fn env_file(mut self, path: impl Into) -> Self { - self.env_file = Some(path.into()); - self - } - - /// Enables compatibility mode. - #[must_use] - pub fn compatibility(mut self) -> Self { - self.compatibility = true; - self - } - - /// Enables dry run mode. - #[must_use] - pub fn dry_run(mut self) -> Self { - self.dry_run = true; - self - } - - /// Sets progress output type. - #[must_use] - pub fn progress(mut self, progress: ProgressType) -> Self { - self.progress = Some(progress); - self - } - - /// Sets ANSI mode. - #[must_use] - pub fn ansi(mut self, ansi: AnsiMode) -> Self { - self.ansi = Some(ansi); - self - } - - /// Sets max parallelism. - #[must_use] - pub fn parallel(mut self, parallel: i32) -> Self { - self.parallel = Some(parallel); - self - } - - /// Builds global compose arguments. - #[must_use] - pub fn build_global_args(&self) -> Vec { - let mut args = Vec::new(); - - // Adds compose files. - for file in &self.files { - args.push("--file".to_string()); - args.push(file.to_string_lossy().to_string()); - } - - // Adds project name. - if let Some(ref name) = self.project_name { - args.push("--project-name".to_string()); - args.push(name.clone()); - } - - // Adds project directory. - if let Some(ref dir) = self.project_directory { - args.push("--project-directory".to_string()); - args.push(dir.to_string_lossy().to_string()); - } - - // Adds profiles. - for profile in &self.profiles { - args.push("--profile".to_string()); - args.push(profile.clone()); - } - - // Adds environment file. - if let Some(ref env_file) = self.env_file { - args.push("--env-file".to_string()); - args.push(env_file.to_string_lossy().to_string()); - } - - // Adds flags. - if self.compatibility { - args.push("--compatibility".to_string()); - } - - if self.dry_run { - args.push("--dry-run".to_string()); - } - - // Adds progress type. - if let Some(progress) = self.progress { - args.push("--progress".to_string()); - args.push(progress.to_string()); - } - - // Adds ANSI mode. - if let Some(ansi) = self.ansi { - args.push("--ansi".to_string()); - args.push(ansi.to_string()); - } - - // Adds parallel limit. - if let Some(parallel) = self.parallel { - args.push("--parallel".to_string()); - args.push(parallel.to_string()); - } - - args - } -} - -/// Extended trait for Docker Compose commands. -pub trait ComposeCommand: DockerCommand { - /// Gets the compose configuration. - fn config(&self) -> &ComposeConfig; - - /// Gets the mutable compose configuration for builder pattern. - fn config_mut(&mut self) -> &mut ComposeConfig; - - /// Gets the compose subcommand name (e.g., `up`, `down`, `ps`). - fn subcommand(&self) -> &'static str; - - /// Builds command-specific arguments (without global compose args). - fn build_subcommand_args(&self) -> Vec; - - /// Builds complete command arguments including "compose" and global args. - /// This provides the implementation for `DockerCommandV2::build_command_args`. - fn build_command_args(&self) -> Vec { - let mut args = vec!["compose".to_string()]; - - // Adds global compose arguments. - args.extend(self.config().build_global_args()); - - // Adds the subcommand. - args.push(self.subcommand().to_string()); - - // Adds command-specific arguments. - args.extend(self.build_subcommand_args()); - - // Adds raw arguments from executor. - args.extend(self.executor().raw_args.clone()); - - args - } - - /// Helper builder methods for common compose config options. - #[must_use] - fn file>(mut self, file: P) -> Self - where - Self: Sized, - { - self.config_mut().files.push(file.into()); - self - } - - /// Sets project name for compose command. - #[must_use] - fn project_name(mut self, name: impl Into) -> Self - where - Self: Sized, - { - self.config_mut().project_name = Some(name.into()); - self - } -} - /// Common functionality for executing Docker commands. #[derive(Debug, Clone)] pub struct CommandExecutor { diff --git a/src/command/compose.rs b/src/command/compose.rs new file mode 100644 index 00000000..895bf5c0 --- /dev/null +++ b/src/command/compose.rs @@ -0,0 +1,30 @@ +// re-exports all compose command modules +pub mod attach; +pub mod build; +pub mod config; +pub mod convert; +pub mod cp; +pub mod create; +pub mod down; +pub mod events; +pub mod exec; +pub mod images; +pub mod kill; +pub mod logs; +pub mod ls; +pub mod pause; +pub mod port; +pub mod ps; +pub mod push; +pub mod restart; +pub mod rm; +pub mod run; +pub mod scale; +pub mod start; +pub mod stop; +pub mod top; +pub mod unpause; +pub mod up; +pub mod version; +pub mod wait; +pub mod watch; diff --git a/src/command/compose_attach.rs b/src/command/compose/attach.rs similarity index 100% rename from src/command/compose_attach.rs rename to src/command/compose/attach.rs diff --git a/src/command/compose_build.rs b/src/command/compose/build.rs similarity index 100% rename from src/command/compose_build.rs rename to src/command/compose/build.rs diff --git a/src/command/compose_config.rs b/src/command/compose/config.rs similarity index 100% rename from src/command/compose_config.rs rename to src/command/compose/config.rs diff --git a/src/command/compose_convert.rs b/src/command/compose/convert.rs similarity index 100% rename from src/command/compose_convert.rs rename to src/command/compose/convert.rs diff --git a/src/command/compose_cp.rs b/src/command/compose/cp.rs similarity index 100% rename from src/command/compose_cp.rs rename to src/command/compose/cp.rs diff --git a/src/command/compose_create.rs b/src/command/compose/create.rs similarity index 100% rename from src/command/compose_create.rs rename to src/command/compose/create.rs diff --git a/src/command/compose_down.rs b/src/command/compose/down.rs similarity index 100% rename from src/command/compose_down.rs rename to src/command/compose/down.rs diff --git a/src/command/compose_events.rs b/src/command/compose/events.rs similarity index 100% rename from src/command/compose_events.rs rename to src/command/compose/events.rs diff --git a/src/command/compose_exec.rs b/src/command/compose/exec.rs similarity index 100% rename from src/command/compose_exec.rs rename to src/command/compose/exec.rs diff --git a/src/command/compose_images.rs b/src/command/compose/images.rs similarity index 100% rename from src/command/compose_images.rs rename to src/command/compose/images.rs diff --git a/src/command/compose_kill.rs b/src/command/compose/kill.rs similarity index 100% rename from src/command/compose_kill.rs rename to src/command/compose/kill.rs diff --git a/src/command/compose_logs.rs b/src/command/compose/logs.rs similarity index 100% rename from src/command/compose_logs.rs rename to src/command/compose/logs.rs diff --git a/src/command/compose_ls.rs b/src/command/compose/ls.rs similarity index 100% rename from src/command/compose_ls.rs rename to src/command/compose/ls.rs diff --git a/src/command/compose_pause.rs b/src/command/compose/pause.rs similarity index 100% rename from src/command/compose_pause.rs rename to src/command/compose/pause.rs diff --git a/src/command/compose_port.rs b/src/command/compose/port.rs similarity index 100% rename from src/command/compose_port.rs rename to src/command/compose/port.rs diff --git a/src/command/compose_ps.rs b/src/command/compose/ps.rs similarity index 100% rename from src/command/compose_ps.rs rename to src/command/compose/ps.rs diff --git a/src/command/compose_push.rs b/src/command/compose/push.rs similarity index 100% rename from src/command/compose_push.rs rename to src/command/compose/push.rs diff --git a/src/command/compose_restart.rs b/src/command/compose/restart.rs similarity index 100% rename from src/command/compose_restart.rs rename to src/command/compose/restart.rs diff --git a/src/command/compose_rm.rs b/src/command/compose/rm.rs similarity index 100% rename from src/command/compose_rm.rs rename to src/command/compose/rm.rs diff --git a/src/command/compose_run.rs b/src/command/compose/run.rs similarity index 100% rename from src/command/compose_run.rs rename to src/command/compose/run.rs diff --git a/src/command/compose_scale.rs b/src/command/compose/scale.rs similarity index 100% rename from src/command/compose_scale.rs rename to src/command/compose/scale.rs diff --git a/src/command/compose_start.rs b/src/command/compose/start.rs similarity index 100% rename from src/command/compose_start.rs rename to src/command/compose/start.rs diff --git a/src/command/compose_stop.rs b/src/command/compose/stop.rs similarity index 100% rename from src/command/compose_stop.rs rename to src/command/compose/stop.rs diff --git a/src/command/compose_top.rs b/src/command/compose/top.rs similarity index 100% rename from src/command/compose_top.rs rename to src/command/compose/top.rs diff --git a/src/command/compose_unpause.rs b/src/command/compose/unpause.rs similarity index 100% rename from src/command/compose_unpause.rs rename to src/command/compose/unpause.rs diff --git a/src/command/compose_up.rs b/src/command/compose/up.rs similarity index 100% rename from src/command/compose_up.rs rename to src/command/compose/up.rs diff --git a/src/command/compose_version.rs b/src/command/compose/version.rs similarity index 100% rename from src/command/compose_version.rs rename to src/command/compose/version.rs diff --git a/src/command/compose_wait.rs b/src/command/compose/wait.rs similarity index 100% rename from src/command/compose_wait.rs rename to src/command/compose/wait.rs diff --git a/src/command/compose_watch.rs b/src/command/compose/watch.rs similarity index 100% rename from src/command/compose_watch.rs rename to src/command/compose/watch.rs diff --git a/src/compose.rs b/src/compose.rs index 8eca9e31..54e10aa9 100644 --- a/src/compose.rs +++ b/src/compose.rs @@ -1,173 +1,148 @@ -//! Docker Compose command implementations. -//! -//! This module provides support for Docker Compose commands, enabling -//! multi-container application management. - -use crate::error::Result; -use async_trait::async_trait; use std::path::PathBuf; -use std::process::Stdio; -use tokio::process::Command as TokioCommand; -/// Base configuration for all compose commands +use crate::{ + command::{AnsiMode, ProgressType}, + DockerCommand, +}; + +/// Base configuration for all compose commands. #[derive(Debug, Clone, Default)] pub struct ComposeConfig { - /// Compose file paths (-f, --file) + /// Compose file paths (-f, --file). pub files: Vec, - /// Project name (-p, --project-name) + /// Project name (-p, --project-name). pub project_name: Option, - /// Project directory (--project-directory) + /// Project directory (--project-directory). pub project_directory: Option, - /// Profiles to enable (--profile) + /// Profiles to enable (--profile). pub profiles: Vec, - /// Environment file (--env-file) + /// Environment file (--env-file). pub env_file: Option, - /// Run in compatibility mode + /// Run in compatibility mode. pub compatibility: bool, - /// Execute in dry run mode + /// Execute in dry run mode. pub dry_run: bool, - /// Progress output type + /// Progress output type. pub progress: Option, - /// ANSI control characters + /// ANSI control characters. pub ansi: Option, - /// Max parallelism (-1 for unlimited) + /// Max parallelism (-1 for unlimited). pub parallel: Option, } -/// Progress output type for compose commands -#[derive(Debug, Clone, Copy)] -pub enum ProgressType { - /// Auto-detect - Auto, - /// TTY output - Tty, - /// Plain text output - Plain, - /// JSON output - Json, - /// Quiet mode - Quiet, -} - -impl std::fmt::Display for ProgressType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Auto => write!(f, "auto"), - Self::Tty => write!(f, "tty"), - Self::Plain => write!(f, "plain"), - Self::Json => write!(f, "json"), - Self::Quiet => write!(f, "quiet"), - } - } -} - -/// ANSI control character mode -#[derive(Debug, Clone, Copy)] -pub enum AnsiMode { - /// Never print ANSI - Never, - /// Always print ANSI - Always, - /// Auto-detect - Auto, -} - -impl std::fmt::Display for AnsiMode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Never => write!(f, "never"), - Self::Always => write!(f, "always"), - Self::Auto => write!(f, "auto"), - } - } -} - impl ComposeConfig { - /// Create a new compose configuration + /// Creates a new compose configuration. #[must_use] pub fn new() -> Self { Self::default() } - /// Add a compose file + /// Adds a compose file. #[must_use] pub fn file(mut self, path: impl Into) -> Self { self.files.push(path.into()); self } - /// Set project name + /// Sets the project name. #[must_use] pub fn project_name(mut self, name: impl Into) -> Self { self.project_name = Some(name.into()); self } - /// Set project directory + /// Sets the project directory. #[must_use] pub fn project_directory(mut self, dir: impl Into) -> Self { self.project_directory = Some(dir.into()); self } - /// Add a profile + /// Adds a profile. #[must_use] pub fn profile(mut self, profile: impl Into) -> Self { self.profiles.push(profile.into()); self } - /// Set environment file + /// Sets environment file. #[must_use] pub fn env_file(mut self, path: impl Into) -> Self { self.env_file = Some(path.into()); self } - /// Enable compatibility mode + /// Enables compatibility mode. + #[must_use] + pub fn compatibility(mut self) -> Self { + self.compatibility = true; + self + } + + /// Enables dry run mode. + #[must_use] + pub fn dry_run(mut self) -> Self { + self.dry_run = true; + self + } + + /// Sets progress output type. #[must_use] - pub fn compatibility(mut self, enabled: bool) -> Self { - self.compatibility = enabled; + pub fn progress(mut self, progress: ProgressType) -> Self { + self.progress = Some(progress); self } - /// Enable dry run mode + /// Sets ANSI mode. #[must_use] - pub fn dry_run(mut self, enabled: bool) -> Self { - self.dry_run = enabled; + pub fn ansi(mut self, ansi: AnsiMode) -> Self { + self.ansi = Some(ansi); self } - /// Build global arguments for compose commands + /// Sets max parallelism. + #[must_use] + pub fn parallel(mut self, parallel: i32) -> Self { + self.parallel = Some(parallel); + self + } + + /// Builds global compose arguments. #[must_use] pub fn build_global_args(&self) -> Vec { let mut args = Vec::new(); + // Adds compose files. for file in &self.files { args.push("--file".to_string()); - args.push(file.display().to_string()); + args.push(file.to_string_lossy().to_string()); } + // Adds project name. if let Some(ref name) = self.project_name { args.push("--project-name".to_string()); args.push(name.clone()); } + // Adds project directory. if let Some(ref dir) = self.project_directory { args.push("--project-directory".to_string()); - args.push(dir.display().to_string()); + args.push(dir.to_string_lossy().to_string()); } + // Adds profiles. for profile in &self.profiles { args.push("--profile".to_string()); args.push(profile.clone()); } + // Adds environment file. if let Some(ref env_file) = self.env_file { args.push("--env-file".to_string()); - args.push(env_file.display().to_string()); + args.push(env_file.to_string_lossy().to_string()); } + // Adds flags. if self.compatibility { args.push("--compatibility".to_string()); } @@ -176,16 +151,19 @@ impl ComposeConfig { args.push("--dry-run".to_string()); } - if let Some(ref progress) = self.progress { + // Adds progress type. + if let Some(progress) = self.progress { args.push("--progress".to_string()); args.push(progress.to_string()); } - if let Some(ref ansi) = self.ansi { + // Adds ANSI mode. + if let Some(ansi) = self.ansi { args.push("--ansi".to_string()); args.push(ansi.to_string()); } + // Adds parallel limit. if let Some(parallel) = self.parallel { args.push("--parallel".to_string()); args.push(parallel.to_string()); @@ -195,424 +173,57 @@ impl ComposeConfig { } } -/// Execute a compose command with the given configuration and arguments -async fn execute_compose_command( - config: &ComposeConfig, - subcommand: &str, - args: Vec, -) -> Result { - let mut cmd = TokioCommand::new("docker"); - - // Add "compose" as the first argument - cmd.arg("compose"); - - // Add global compose arguments - for arg in config.build_global_args() { - cmd.arg(arg); - } - - // Add the subcommand - cmd.arg(subcommand); - - // Add command-specific arguments - for arg in args { - cmd.arg(arg); - } - - // Set up output pipes - cmd.stdout(Stdio::piped()); - cmd.stderr(Stdio::piped()); - - let output = cmd.output().await.map_err(|e| { - crate::error::Error::custom(format!( - "Failed to execute docker compose {subcommand}: {e}" - )) - })?; - - let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let success = output.status.success(); - let exit_code = output.status.code().unwrap_or(-1); - - if !success && !stderr.contains("Gracefully stopping...") { - return Err(crate::error::Error::command_failed( - format!("docker compose {subcommand}"), - exit_code, - stdout.clone(), - stderr.clone(), - )); - } - - Ok(ComposeOutput { - stdout, - stderr, - exit_code, - success, - }) -} - -/// Common trait for all compose commands (existing pattern) -#[async_trait] -pub trait ComposeCommand { - /// The output type this command produces - type Output; - - /// Get the compose subcommand name (e.g., "up", "down", "ps") - fn subcommand(&self) -> &'static str; - - /// Build command-specific arguments - fn build_args(&self) -> Vec; - - /// Execute the command - async fn execute(&self) -> Result; - - /// Get the compose configuration +/// Extended trait for Docker Compose commands. +pub trait ComposeCommand: DockerCommand { + /// Gets the compose configuration. fn config(&self) -> &ComposeConfig; -} - -/// Common trait for new compose commands -#[async_trait] -pub trait ComposeCommandV2 { - /// The output type this command produces - type Output; - /// Get the compose configuration - fn get_config(&self) -> &ComposeConfig; + /// Gets the mutable compose configuration for builder pattern. + fn config_mut(&mut self) -> &mut ComposeConfig; - /// Get mutable compose configuration - fn get_config_mut(&mut self) -> &mut ComposeConfig; - - /// Execute compose command with given arguments - async fn execute_compose(&self, args: Vec) -> Result; + /// Gets the compose subcommand name (e.g., `up`, `down`, `ps`). + fn subcommand(&self) -> &'static str; - /// Execute the command - async fn execute(&self) -> Result; + /// Builds command-specific arguments (without global compose args). + fn build_subcommand_args(&self) -> Vec; - /// Helper to execute compose command - async fn execute_compose_command(&self, args: Vec) -> Result { - let config = self.get_config(); - let mut cmd = TokioCommand::new("docker"); + /// Builds complete command arguments including "compose" and global args. + /// This provides the implementation for `DockerCommandV2::build_command_args`. + fn build_command_args(&self) -> Vec { + let mut args = vec!["compose".to_string()]; - // Add "compose" as the first argument - cmd.arg("compose"); + // Adds global compose arguments. + args.extend(self.config().build_global_args()); - // Add global compose arguments - for arg in config.build_global_args() { - cmd.arg(arg); - } + // Adds the subcommand. + args.push(self.subcommand().to_string()); - // Add command-specific arguments - for arg in args { - cmd.arg(arg); - } + // Adds command-specific arguments. + args.extend(self.build_subcommand_args()); - // Set up output pipes - cmd.stdout(Stdio::piped()); - cmd.stderr(Stdio::piped()); - - let output = cmd.output().await.map_err(|e| { - crate::error::Error::custom(format!("Failed to execute docker compose: {e}")) - })?; - - let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - let success = output.status.success(); - let exit_code = output.status.code().unwrap_or(-1); - - if !success && !stderr.contains("Gracefully stopping...") { - return Err(crate::error::Error::command_failed( - "docker compose".to_string(), - exit_code, - stdout.clone(), - stderr.clone(), - )); - } + // Adds raw arguments from executor. + args.extend(self.executor().raw_args.clone()); - Ok(ComposeOutput { - stdout, - stderr, - exit_code, - success, - }) + args } -} - -/// Output from a compose command -#[derive(Debug, Clone)] -pub struct ComposeOutput { - /// Standard output - pub stdout: String, - /// Standard error - pub stderr: String, - /// Exit code - pub exit_code: i32, - /// Whether the command succeeded - pub success: bool, -} -impl ComposeOutput { - /// Get stdout lines + /// Helper builder methods for common compose config options. #[must_use] - pub fn stdout_lines(&self) -> Vec<&str> { - self.stdout.lines().collect() + fn file>(mut self, file: P) -> Self + where + Self: Sized, + { + self.config_mut().files.push(file.into()); + self } - /// Get stderr lines + /// Sets project name for compose command. #[must_use] - pub fn stderr_lines(&self) -> Vec<&str> { - self.stderr.lines().collect() - } -} - -// Re-export submodules -pub mod attach; -pub mod build; -pub mod config; -pub mod convert; -pub mod cp; -pub mod create; -pub mod down; -pub mod events; -pub mod exec; -pub mod images; -pub mod kill; -pub mod logs; -pub mod ls; -pub mod pause; -pub mod port; -pub mod ps; -pub mod push; -pub mod restart; -pub mod rm; -pub mod run; -pub mod scale; -pub mod start; -pub mod stop; -pub mod top; -pub mod unpause; -pub mod up; -pub mod version; -pub mod wait; -pub mod watch; - -pub use attach::{AttachResult, ComposeAttachCommand}; -pub use build::ComposeBuildCommand; -pub use config::{ComposeConfigCommand, ConfigFormat, ConfigResult}; -pub use convert::{ComposeConvertCommand, ConvertFormat, ConvertResult}; -pub use cp::{ComposeCpCommand, CpResult}; -pub use create::{ComposeCreateCommand, CreateResult, PullPolicy}; -pub use down::ComposeDownCommand; -pub use events::{ComposeEvent, ComposeEventsCommand, EventsResult}; -pub use exec::ComposeExecCommand; -pub use images::{ComposeImagesCommand, ImageInfo, ImagesFormat, ImagesResult}; -pub use kill::{ComposeKillCommand, KillResult}; -pub use logs::ComposeLogsCommand; -pub use ls::{ComposeLsCommand, ComposeProject, LsFormat, LsResult}; -pub use pause::{ComposePauseCommand, PauseResult}; -pub use port::{ComposePortCommand, PortResult}; -pub use ps::ComposePsCommand; -pub use push::{ComposePushCommand, PushResult}; -pub use restart::ComposeRestartCommand; -pub use rm::{ComposeRmCommand, RmResult}; -pub use run::ComposeRunCommand; -pub use scale::{ComposeScaleCommand, ScaleResult}; -pub use start::ComposeStartCommand; -pub use stop::ComposeStopCommand; -pub use top::{ComposeTopCommand, TopResult}; -pub use unpause::{ComposeUnpauseCommand, UnpauseResult}; -pub use up::ComposeUpCommand; -pub use version::{ComposeVersionCommand, VersionFormat, VersionInfo, VersionResult}; -pub use wait::{ComposeWaitCommand, WaitResult}; -pub use watch::{ComposeWatchCommand, WatchResult}; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compose_config_new() { - let config = ComposeConfig::new(); - assert!(config.files.is_empty()); - assert!(config.project_name.is_none()); - assert!(config.project_directory.is_none()); - assert!(config.profiles.is_empty()); - assert!(config.env_file.is_none()); - assert!(!config.compatibility); - assert!(!config.dry_run); - } - - #[test] - fn test_compose_config_builder() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .file("docker-compose.override.yml") - .project_name("myproject") - .project_directory("/path/to/project") - .profile("dev") - .profile("debug") - .env_file(".env") - .compatibility(true) - .dry_run(true); - - assert_eq!(config.files.len(), 2); - assert_eq!(config.files[0].to_str().unwrap(), "docker-compose.yml"); - assert_eq!( - config.files[1].to_str().unwrap(), - "docker-compose.override.yml" - ); - assert_eq!(config.project_name, Some("myproject".to_string())); - assert_eq!( - config.project_directory.unwrap().to_str().unwrap(), - "/path/to/project" - ); - assert_eq!(config.profiles, vec!["dev", "debug"]); - assert_eq!(config.env_file.unwrap().to_str().unwrap(), ".env"); - assert!(config.compatibility); - assert!(config.dry_run); - } - - #[test] - fn test_compose_config_build_global_args() { - let config = ComposeConfig::new(); - let args = config.build_global_args(); - assert!(args.is_empty()); - } - - #[test] - fn test_compose_config_build_global_args_with_files() { - let config = ComposeConfig::new() - .file("compose.yml") - .file("compose.override.yml"); - - let args = config.build_global_args(); - assert_eq!( - args, - vec!["--file", "compose.yml", "--file", "compose.override.yml"] - ); - } - - #[test] - fn test_compose_config_build_global_args_complete() { - let config = ComposeConfig::new() - .file("docker-compose.yml") - .project_name("test") - .project_directory("/app") - .profile("prod") - .env_file(".env.prod") - .compatibility(true) - .dry_run(true); - - let args = config.build_global_args(); - assert!(args.contains(&"--file".to_string())); - assert!(args.contains(&"docker-compose.yml".to_string())); - assert!(args.contains(&"--project-name".to_string())); - assert!(args.contains(&"test".to_string())); - assert!(args.contains(&"--project-directory".to_string())); - assert!(args.contains(&"/app".to_string())); - assert!(args.contains(&"--profile".to_string())); - assert!(args.contains(&"prod".to_string())); - assert!(args.contains(&"--env-file".to_string())); - assert!(args.contains(&".env.prod".to_string())); - assert!(args.contains(&"--compatibility".to_string())); - assert!(args.contains(&"--dry-run".to_string())); - } - - #[test] - fn test_compose_config_with_progress() { - let mut config = ComposeConfig::new(); - config.progress = Some(ProgressType::Plain); - - let args = config.build_global_args(); - assert!(args.contains(&"--progress".to_string())); - assert!(args.contains(&"plain".to_string())); - } - - #[test] - fn test_compose_config_with_ansi() { - let mut config = ComposeConfig::new(); - config.ansi = Some(AnsiMode::Never); - - let args = config.build_global_args(); - assert!(args.contains(&"--ansi".to_string())); - assert!(args.contains(&"never".to_string())); - } - - #[test] - fn test_compose_config_with_parallel() { - let mut config = ComposeConfig::new(); - config.parallel = Some(4); - - let args = config.build_global_args(); - assert!(args.contains(&"--parallel".to_string())); - assert!(args.contains(&"4".to_string())); - } - - #[test] - fn test_progress_type_display() { - assert_eq!(ProgressType::Auto.to_string(), "auto"); - assert_eq!(ProgressType::Tty.to_string(), "tty"); - assert_eq!(ProgressType::Plain.to_string(), "plain"); - assert_eq!(ProgressType::Json.to_string(), "json"); - assert_eq!(ProgressType::Quiet.to_string(), "quiet"); - } - - #[test] - fn test_ansi_mode_display() { - assert_eq!(AnsiMode::Never.to_string(), "never"); - assert_eq!(AnsiMode::Always.to_string(), "always"); - assert_eq!(AnsiMode::Auto.to_string(), "auto"); - } - - #[test] - fn test_compose_output_stdout_lines() { - let output = ComposeOutput { - stdout: "line1\nline2\nline3".to_string(), - stderr: String::new(), - exit_code: 0, - success: true, - }; - - let lines = output.stdout_lines(); - assert_eq!(lines, vec!["line1", "line2", "line3"]); - } - - #[test] - fn test_compose_output_stderr_lines() { - let output = ComposeOutput { - stdout: String::new(), - stderr: "error1\nerror2".to_string(), - exit_code: 1, - success: false, - }; - - let lines = output.stderr_lines(); - assert_eq!(lines, vec!["error1", "error2"]); - } - - #[test] - fn test_compose_output_empty_lines() { - let output = ComposeOutput { - stdout: String::new(), - stderr: String::new(), - exit_code: 0, - success: true, - }; - - // Empty string produces no lines when split - assert!(output.stdout_lines().is_empty()); - assert!(output.stderr_lines().is_empty()); - } - - #[test] - fn test_compose_output_single_line() { - let output = ComposeOutput { - stdout: "single line".to_string(), - stderr: "error line".to_string(), - exit_code: 0, - success: true, - }; - - assert_eq!(output.stdout_lines(), vec!["single line"]); - assert_eq!(output.stderr_lines(), vec!["error line"]); + fn project_name(mut self, name: impl Into) -> Self + where + Self: Sized, + { + self.config_mut().project_name = Some(name.into()); + self } } diff --git a/src/lib.rs b/src/lib.rs index a97fb6b4..66655377 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,15 +8,15 @@ //! //! ## Features //! -//! - **Complete Docker CLI coverage**: Implements all 35 essential Docker commands -//! - **Type-safe builder pattern**: Compile-time validation of command construction -//! - **Async/await support**: Built on Tokio for efficient async operations -//! - **Streaming support**: Real-time output streaming for long-running commands -//! - **Docker Compose support**: Optional feature for multi-container orchestration -//! - **Container templates**: Pre-configured templates for Redis, `PostgreSQL`, `MongoDB`, etc. -//! - **Zero dependencies on Docker SDK**: Works directly with the Docker CLI -//! - **Comprehensive error handling**: Detailed error messages and types -//! - **Well-tested**: Extensive unit and integration test coverage +//! - **Complete Docker CLI coverage**: Implements all 35 essential Docker commands; +//! - **Type-safe builder pattern**: Compile-time validation of command construction; +//! - **Async/await support**: Built on Tokio for efficient async operations; +//! - **Streaming support**: Real-time output streaming for long-running commands; +//! - **Docker Compose support**: Optional feature for multi-container orchestration; +//! - **Container templates**: Pre-configured templates for Redis, `PostgreSQL`, `MongoDB`, etc.; +//! - **Zero dependencies on Docker SDK**: Works directly with the Docker CLI; +//! - **Comprehensive error handling**: Detailed error messages and types; +//! - **Well-tested**: Extensive unit and integration test coverage. //! //! ## Quick Start //! @@ -229,6 +229,7 @@ //! ## Command Coverage //! //! ### Container Commands +//! //! - `run` - Run a new container //! - `exec` - Execute commands in running containers //! - `ps` - List containers @@ -254,6 +255,7 @@ //! - `commit` - Create image from container //! //! ### Image Commands +//! //! - `images` - List images //! - `pull` - Pull images from registry //! - `push` - Push images to registry @@ -268,6 +270,7 @@ //! - `search` - Search Docker Hub for images //! //! ### System Commands +//! //! - `info` - Display system information //! - `version` - Show Docker version //! - `events` - Monitor Docker events @@ -303,13 +306,13 @@ //! # use docker_wrapper::{DockerCommand, RunCommand, RmCommand}; //! # #[tokio::main] //! # async fn main() -> Result<(), Box> { -//! // Use auto-remove for temporary containers +//! // use auto-remove for temporary containers //! RunCommand::new("alpine") -//! .remove() // Automatically remove when stopped +//! .remove() // automatically removes when stopped //! .execute() //! .await?; //! -//! // Or manually remove containers +//! // or manually remove containers //! RmCommand::new("my-container") //! .force() //! .execute() @@ -348,11 +351,11 @@ //! //! The `examples/` directory contains comprehensive examples: //! -//! - `basic_usage.rs` - Common Docker operations -//! - `container_lifecycle.rs` - Container management patterns -//! - `docker_compose.rs` - Docker Compose usage -//! - `streaming.rs` - Real-time output streaming -//! - `error_handling.rs` - Error handling patterns +//! - `basic_usage.rs` - Common Docker operations; +//! - `container_lifecycle.rs` - Container management patterns; +//! - `docker_compose.rs` - Docker Compose usage; +//! - `streaming.rs` - Real-time output streaming; +//! - `error_handling.rs` - Error handling patterns. //! //! Run examples with: //! @@ -367,11 +370,13 @@ //! Migrating from shell scripts to `docker-wrapper` is straightforward: //! //! **Shell:** +//! //! ```bash //! docker run -d --name web -p 8080:80 nginx:latest //! ``` //! //! **Rust:** +//! //! ```rust,no_run //! # use docker_wrapper::{DockerCommand, RunCommand}; //! # #[tokio::main] From 796fb6acdcecb6f254b5ec22f1f1afc7d69fde68 Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 15:32:18 +0800 Subject: [PATCH 8/9] feat: compose commands --- examples/docker_compose.rs | 4 +- examples/exec_examples.rs | 2 +- src/command.rs | 6 +- src/command/bake.rs | 6 +- src/command/compose/attach.rs | 54 ++++++------ src/command/compose/build.rs | 105 ++++++++++++----------- src/command/compose/config.rs | 115 +++++++++++++------------ src/command/compose/convert.rs | 70 ++++++++------- src/command/compose/cp.rs | 69 ++++++++------- src/command/compose/create.rs | 91 +++++++++++--------- src/command/compose/down.rs | 76 +++++++++-------- src/command/compose/events.rs | 89 ++++++++++--------- src/command/compose/exec.rs | 108 +++++++++++++----------- src/command/compose/images.rs | 92 +++++++++++--------- src/command/compose/kill.rs | 54 ++++++------ src/command/compose/logs.rs | 80 ++++++++++-------- src/command/compose/ls.rs | 91 +++++++++++--------- src/command/compose/pause.rs | 49 ++++++----- src/command/compose/port.rs | 66 ++++++++------- src/command/compose/ps.rs | 118 ++++++++++++++------------ src/command/compose/push.rs | 67 ++++++++------- src/command/compose/restart.rs | 54 ++++++------ src/command/compose/rm.rs | 68 ++++++++------- src/command/compose/run.rs | 150 +++++++++++++++++---------------- src/command/compose/scale.rs | 59 +++++++------ src/command/compose/start.rs | 49 ++++++----- src/command/compose/stop.rs | 52 +++++++----- src/command/compose/top.rs | 55 ++++++------ src/command/compose/unpause.rs | 49 ++++++----- src/command/compose/up.rs | 139 +++++++++++++++--------------- src/command/compose/version.rs | 80 ++++++++++-------- src/command/compose/wait.rs | 66 ++++++++------- src/command/compose/watch.rs | 62 ++++++++------ src/compose.rs | 24 +++--- tests/images_integration.rs | 2 +- 35 files changed, 1254 insertions(+), 1067 deletions(-) diff --git a/examples/docker_compose.rs b/examples/docker_compose.rs index e4044b5c..046e9acd 100644 --- a/examples/docker_compose.rs +++ b/examples/docker_compose.rs @@ -1,9 +1,9 @@ -//! Docker Compose example demonstrating multi-container application management +//! Docker Compose example demonstrating multi-container application management. //! //! This example shows how to use the compose feature to manage //! multi-container applications with Docker Compose. //! -//! Run with: cargo run --example docker_compose --features compose +//! Run with: cargo run --example docker_compose --features compose. #[cfg(feature = "compose")] use docker_wrapper::compose::down::RemoveImages; diff --git a/examples/exec_examples.rs b/examples/exec_examples.rs index c22168cc..3bbbdbd8 100644 --- a/examples/exec_examples.rs +++ b/examples/exec_examples.rs @@ -175,7 +175,7 @@ async fn env_exec_example(container_id: &str) { } } - // Multiple environment variables from HashMap + // multiple environment variables from HashMap let mut env_vars = HashMap::new(); env_vars.insert("DEBUG".to_string(), "true".to_string()); env_vars.insert("LOG_LEVEL".to_string(), "info".to_string()); diff --git a/src/command.rs b/src/command.rs index ce25f130..3ea15d74 100644 --- a/src/command.rs +++ b/src/command.rs @@ -124,9 +124,10 @@ pub trait DockerCommand { } /// Progress output type for compose commands. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy)] pub enum ProgressType { /// Auto-detects progress output. + #[default] Auto, /// TTY output. Tty, @@ -151,13 +152,14 @@ impl std::fmt::Display for ProgressType { } /// ANSI control character mode. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Default, Clone, Copy)] pub enum AnsiMode { /// Never prints ANSI. Never, /// Always prints ANSI. Always, /// Auto-detects ANSI. + #[default] Auto, } diff --git a/src/command/bake.rs b/src/command/bake.rs index 1d2a59ea..26240754 100644 --- a/src/command/bake.rs +++ b/src/command/bake.rs @@ -171,7 +171,7 @@ impl BakeCommand { /// Add a target to build /// - /// Multiple targets can be specified. If no targets are specified, + /// multiple targets can be specified. If no targets are specified, /// all targets defined in the bake file will be built. /// /// # Examples @@ -941,9 +941,7 @@ mod tests { fn test_bake_command_extensibility() { let mut bake_cmd = BakeCommand::new(); bake_cmd.executor_mut().add_arg("--experimental"); - bake_cmd - .executor_mut() - .add_args(vec!["--custom", "value"]); + bake_cmd.executor_mut().add_args(vec!["--custom", "value"]); // Extensibility is handled through the executor's raw_args // The actual testing of raw args is done in command.rs tests diff --git a/src/command/compose/attach.rs b/src/command/compose/attach.rs index 07d739de..7e60e502 100644 --- a/src/command/compose/attach.rs +++ b/src/command/compose/attach.rs @@ -1,41 +1,44 @@ //! Docker Compose attach command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose attach command +/// Docker Compose attach command. /// /// Attach to a running container's output. #[derive(Debug, Clone, Default)] pub struct ComposeAttachCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Service to attach to + /// Service to attach to. pub service: String, - /// Detach keys sequence + /// Detach keys sequence. pub detach_keys: Option, - /// Container index if service has multiple instances + /// Container index if service has multiple instances. pub index: Option, - /// Don't stream STDIN + /// Doesn't stream STDIN. pub no_stdin: bool, - /// Use a pseudo-TTY + /// Uses a pseudo-TTY. pub sig_proxy: bool, } -/// Result from attach command +/// Result from attach command. #[derive(Debug, Clone)] pub struct AttachResult { - /// Output from the command + /// Output from the command. pub output: String, - /// Whether the operation succeeded + /// Whether the operation succeeded. pub success: bool, } impl ComposeAttachCommand { - /// Create a new attach command + /// Creates a new attach command. #[must_use] pub fn new(service: impl Into) -> Self { Self { @@ -47,28 +50,28 @@ impl ComposeAttachCommand { } } - /// Set detach keys + /// Sets detach keys. #[must_use] pub fn detach_keys(mut self, keys: impl Into) -> Self { self.detach_keys = Some(keys.into()); self } - /// Set container index + /// Sets container index. #[must_use] pub fn index(mut self, index: u32) -> Self { self.index = Some(index); self } - /// Don't attach to STDIN + /// Doesn't attach to STDIN. #[must_use] pub fn no_stdin(mut self) -> Self { self.no_stdin = true; self } - /// Disable signal proxy + /// Disables signal proxy. #[must_use] pub fn no_sig_proxy(mut self) -> Self { self.sig_proxy = false; @@ -80,6 +83,10 @@ impl ComposeAttachCommand { impl DockerCommand for ComposeAttachCommand { type Output = AttachResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -89,7 +96,6 @@ impl DockerCommand for ComposeAttachCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -105,6 +111,10 @@ impl DockerCommand for ComposeAttachCommand { } impl ComposeCommand for ComposeAttachCommand { + fn subcommand_name() -> &'static str { + "attach" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -113,14 +123,10 @@ impl ComposeCommand for ComposeAttachCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "attach" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); - // Add flags + // add flags if let Some(ref keys) = self.detach_keys { args.push("--detach-keys".to_string()); args.push(keys.clone()); diff --git a/src/command/compose/build.rs b/src/command/compose/build.rs index b6a0eccc..134d0918 100644 --- a/src/command/compose/build.rs +++ b/src/command/compose/build.rs @@ -1,46 +1,50 @@ //! Docker Compose build command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::collections::HashMap; -/// Docker Compose build command builder +/// Docker Compose build command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for build command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for build command pub struct ComposeBuildCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to build (empty for all) + /// Services to build (empty for all). pub services: Vec, - /// Do not use cache when building the image + /// Doesn't use cache when building the image. pub no_cache: bool, - /// Always attempt to pull a newer version of the image + /// Always attempts to pull a newer version of the image. pub pull: bool, - /// Don't print anything to stdout + /// Doesn't print anything to stdout. pub quiet: bool, - /// Set build-time variables + /// Sets build-time variables. pub build_args: HashMap, - /// Build images in parallel + /// Builds images in parallel. pub parallel: bool, - /// Amount of memory for builds + /// Amount of memory for builds. pub memory: Option, - /// Build with `BuildKit` progress output + /// Builds with `BuildKit` progress output. pub progress: Option, - /// Set the SSH agent socket or key + /// Sets the SSH agent socket or key. pub ssh: Option, } -/// Build progress output type -#[derive(Debug, Clone, Copy)] +/// Build progress output type. +#[derive(Debug, Default, Clone, Copy)] pub enum ProgressOutput { - /// Auto-detect + /// Auto-detects progress type. + #[default] Auto, - /// Plain text progress + /// Plain text progress. Plain, - /// TTY progress + /// TTY progress. Tty, } @@ -54,21 +58,21 @@ impl std::fmt::Display for ProgressOutput { } } -/// Result from compose build command +/// Result from compose build command. #[derive(Debug, Clone)] pub struct ComposeBuildResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were built + /// Services that were built. pub services: Vec, } impl ComposeBuildCommand { - /// Create a new compose build command + /// Creates a new compose build command. #[must_use] pub fn new() -> Self { Self { @@ -86,14 +90,14 @@ impl ComposeBuildCommand { } } - /// Add a service to build + /// Adds a service to build. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services + /// Adds multiple services. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -104,63 +108,63 @@ impl ComposeBuildCommand { self } - /// Do not use cache when building the image + /// Doesn't use cache when building the image. #[must_use] pub fn no_cache(mut self) -> Self { self.no_cache = true; self } - /// Always attempt to pull a newer version of the image + /// Always attempts to pull a newer version of the image. #[must_use] pub fn pull(mut self) -> Self { self.pull = true; self } - /// Don't print anything to stdout + /// Doesn't print anything to stdout. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; self } - /// Add a build-time variable + /// Adds a build-time variable. #[must_use] pub fn build_arg(mut self, key: impl Into, value: impl Into) -> Self { self.build_args.insert(key.into(), value.into()); self } - /// Add multiple build-time variables + /// Adds multiple build-time variables. #[must_use] pub fn build_args(mut self, args: HashMap) -> Self { self.build_args.extend(args); self } - /// Build images in parallel + /// Builds images in parallel. #[must_use] pub fn parallel(mut self) -> Self { self.parallel = true; self } - /// Set memory limit for builds + /// Sets memory limit for builds. #[must_use] pub fn memory(mut self, memory: impl Into) -> Self { self.memory = Some(memory.into()); self } - /// Set progress output type + /// Sets progress output type. #[must_use] pub fn progress(mut self, progress: ProgressOutput) -> Self { self.progress = Some(progress); self } - /// Set SSH agent socket or key + /// Sets SSH agent socket or key. #[must_use] pub fn ssh(mut self, ssh: impl Into) -> Self { self.ssh = Some(ssh.into()); @@ -178,6 +182,10 @@ impl Default for ComposeBuildCommand { impl DockerCommand for ComposeBuildCommand { type Output = ComposeBuildResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -187,7 +195,6 @@ impl DockerCommand for ComposeBuildCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -205,6 +212,10 @@ impl DockerCommand for ComposeBuildCommand { } impl ComposeCommand for ComposeBuildCommand { + fn subcommand_name() -> &'static str { + "build" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -213,10 +224,6 @@ impl ComposeCommand for ComposeBuildCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "build" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -236,31 +243,31 @@ impl ComposeCommand for ComposeBuildCommand { args.push("--parallel".to_string()); } - // Add build args + // add build args for (key, value) in &self.build_args { args.push("--build-arg".to_string()); args.push(format!("{key}={value}")); } - // Add memory limit + // add memory limit if let Some(ref memory) = self.memory { args.push("--memory".to_string()); args.push(memory.clone()); } - // Add progress output + // add progress output if let Some(progress) = self.progress { args.push("--progress".to_string()); args.push(progress.to_string()); } - // Add SSH configuration + // add SSH configuration if let Some(ref ssh) = self.ssh { args.push("--ssh".to_string()); args.push(ssh.clone()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -268,13 +275,13 @@ impl ComposeCommand for ComposeBuildCommand { } impl ComposeBuildResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were built + /// Gets the services that were built. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/config.rs b/src/command/compose/config.rs index 193d7c79..27bb45d7 100644 --- a/src/command/compose/config.rs +++ b/src/command/compose/config.rs @@ -1,49 +1,53 @@ //! Docker Compose config command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose config command builder +/// Docker Compose config command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for config command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for config command pub struct ComposeConfigCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Output format + /// Output format. pub format: Option, - /// Resolve image digests + /// Resolves image digests. pub resolve_image_digests: bool, - /// Don't interpolate environment variables + /// Doesn't interpolate environment variables. pub no_interpolate: bool, - /// Don't normalize paths + /// Doesn't normalize paths. pub no_normalize: bool, - /// Don't check consistency + /// Doesn't check consistency. pub no_consistency: bool, - /// Show services only + /// Shows services only. pub services: bool, - /// Show volumes only + /// Shows volumes only. pub volumes: bool, - /// Show profiles only + /// Shows profiles only. pub profiles: bool, - /// Show images only + /// Shows images only. pub images: bool, - /// Hash of services to include + /// Hash of services to include. pub hash: Option, - /// Output file path + /// Output file path. pub output: Option, - /// Quiet mode + /// Quiet mode. pub quiet: bool, } -/// Config output format -#[derive(Debug, Clone, Copy)] +/// Config output format. +#[derive(Debug, Default, Clone, Copy)] pub enum ConfigFormat { - /// YAML format (default) + /// YAML format (default). + #[default] Yaml, - /// JSON format + /// JSON format. Json, } @@ -56,21 +60,21 @@ impl std::fmt::Display for ConfigFormat { } } -/// Result from compose config command +/// Result from compose config command. #[derive(Debug, Clone)] pub struct ComposeConfigResult { - /// Raw stdout output (configuration YAML/JSON) + /// Raw stdout output (configuration YAML/JSON). pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Whether configuration is valid + /// Whether configuration is valid. pub is_valid: bool, } impl ComposeConfigCommand { - /// Create a new compose config command + /// Creates a new compose config command. #[must_use] pub fn new() -> Self { Self { @@ -91,98 +95,98 @@ impl ComposeConfigCommand { } } - /// Set output format + /// Sets output format. #[must_use] pub fn format(mut self, format: ConfigFormat) -> Self { self.format = Some(format); self } - /// Set output format to JSON + /// Sets output format to JSON. #[must_use] pub fn format_json(mut self) -> Self { self.format = Some(ConfigFormat::Json); self } - /// Set output format to YAML + /// Sets output format to YAML. #[must_use] pub fn format_yaml(mut self) -> Self { self.format = Some(ConfigFormat::Yaml); self } - /// Resolve image digests + /// Resolves image digests. #[must_use] pub fn resolve_image_digests(mut self) -> Self { self.resolve_image_digests = true; self } - /// Don't interpolate environment variables + /// Doesn't interpolate environment variables. #[must_use] pub fn no_interpolate(mut self) -> Self { self.no_interpolate = true; self } - /// Don't normalize paths + /// Doesn't normalize paths. #[must_use] pub fn no_normalize(mut self) -> Self { self.no_normalize = true; self } - /// Don't check consistency + /// Doesn't check consistency. #[must_use] pub fn no_consistency(mut self) -> Self { self.no_consistency = true; self } - /// Show services only + /// Shows services only. #[must_use] pub fn services(mut self) -> Self { self.services = true; self } - /// Show volumes only + /// Shows volumes only. #[must_use] pub fn volumes(mut self) -> Self { self.volumes = true; self } - /// Show profiles only + /// Shows profiles only. #[must_use] pub fn profiles(mut self) -> Self { self.profiles = true; self } - /// Show images only + /// Shows images only. #[must_use] pub fn images(mut self) -> Self { self.images = true; self } - /// Set hash of services to include + /// Sets hash of services to include. #[must_use] pub fn hash(mut self, hash: impl Into) -> Self { self.hash = Some(hash.into()); self } - /// Set output file path + /// Sets output file path. #[must_use] pub fn output(mut self, output: impl Into) -> Self { self.output = Some(output.into()); self } - /// Enable quiet mode + /// Enables quiet mode. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; @@ -200,16 +204,19 @@ impl Default for ComposeConfigCommand { impl DockerCommand for ComposeConfigCommand { type Output = ComposeConfigResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -227,16 +234,16 @@ impl DockerCommand for ComposeConfigCommand { } impl ComposeCommand for ComposeConfigCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "config" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "config" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -298,19 +305,19 @@ impl ComposeCommand for ComposeConfigCommand { } impl ComposeConfigResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Check if the configuration is valid + /// Checks if the configuration is valid. #[must_use] pub fn is_valid(&self) -> bool { self.is_valid } - /// Get the configuration output + /// Gets the configuration output. #[must_use] pub fn config_output(&self) -> &str { &self.stdout diff --git a/src/command/compose/convert.rs b/src/command/compose/convert.rs index 3dc5cb9a..99d6bf2b 100644 --- a/src/command/compose/convert.rs +++ b/src/command/compose/convert.rs @@ -1,28 +1,32 @@ //! Docker Compose convert command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose convert command builder +/// Docker Compose convert command builder. #[derive(Debug, Clone)] pub struct ComposeConvertCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Output format + /// Output format. pub format: Option, - /// Output file path + /// Output file path. pub output: Option, } -/// Convert output format -#[derive(Debug, Clone, Copy)] +/// Convert output format. +#[derive(Debug, Default, Clone, Copy)] pub enum ConvertFormat { - /// YAML format (default) + /// YAML format (default). + #[default] Yaml, - /// JSON format + /// JSON format. Json, } @@ -35,21 +39,21 @@ impl std::fmt::Display for ConvertFormat { } } -/// Result from compose convert command +/// Result from compose convert command. #[derive(Debug, Clone)] pub struct ComposeConvertResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Converted configuration + /// Converted configuration. pub converted_config: String, } impl ComposeConvertCommand { - /// Create a new compose convert command + /// Creates a new compose convert command. #[must_use] pub fn new() -> Self { Self { @@ -60,28 +64,28 @@ impl ComposeConvertCommand { } } - /// Set output format + /// Sets output format. #[must_use] pub fn format(mut self, format: ConvertFormat) -> Self { self.format = Some(format); self } - /// Set output format to JSON + /// Sets output format to JSON. #[must_use] pub fn format_json(mut self) -> Self { self.format = Some(ConvertFormat::Json); self } - /// Set output format to YAML + /// Sets output format to YAML. #[must_use] pub fn format_yaml(mut self) -> Self { self.format = Some(ConvertFormat::Yaml); self } - /// Set output file path + /// Sets output file path. #[must_use] pub fn output(mut self, output: impl Into) -> Self { self.output = Some(output.into()); @@ -99,11 +103,15 @@ impl Default for ComposeConvertCommand { impl DockerCommand for ComposeConvertCommand { type Output = ComposeConvertResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -125,16 +133,16 @@ impl DockerCommand for ComposeConvertCommand { } impl ComposeCommand for ComposeConvertCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "convert" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "convert" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -155,13 +163,13 @@ impl ComposeCommand for ComposeConvertCommand { } impl ComposeConvertResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the converted configuration + /// Gets the converted configuration. #[must_use] pub fn converted_config(&self) -> &str { &self.converted_config diff --git a/src/command/compose/cp.rs b/src/command/compose/cp.rs index 93b05995..0f58daad 100644 --- a/src/command/compose/cp.rs +++ b/src/command/compose/cp.rs @@ -1,45 +1,48 @@ //! Docker Compose cp command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose cp command builder +/// Docker Compose cp command builder. #[derive(Debug, Clone)] pub struct ComposeCpCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Source path (can be service:path or local path) + /// Source path (can be service:path or local path). pub source: String, - /// Destination path (can be service:path or local path) + /// Destination path (can be service:path or local path). pub destination: String, - /// Archive mode (preserve permissions) + /// Archive mode (preserve permissions). pub archive: bool, - /// Follow symbolic links + /// Follows symbolic links. pub follow_link: bool, - /// Index of the container (if service has multiple instances) + /// Index of the container (if service has multiple instances). pub index: Option, } -/// Result from compose cp command +/// Result from compose cp command. #[derive(Debug, Clone)] pub struct ComposeCpResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Source path used + /// Source path used. pub source: String, - /// Destination path used + /// Destination path used. pub destination: String, } impl ComposeCpCommand { - /// Create a new compose cp command + /// Creates a new compose cp command. #[must_use] pub fn new(source: impl Into, destination: impl Into) -> Self { Self { @@ -53,21 +56,21 @@ impl ComposeCpCommand { } } - /// Enable archive mode (preserve permissions and ownership) + /// Enables archive mode (preserves permissions and ownership). #[must_use] pub fn archive(mut self) -> Self { self.archive = true; self } - /// Follow symbolic links in source path + /// Follows symbolic links in source path. #[must_use] pub fn follow_link(mut self) -> Self { self.follow_link = true; self } - /// Set container index if service has multiple instances + /// Sets container index if service has multiple instances. #[must_use] pub fn index(mut self, index: u32) -> Self { self.index = Some(index); @@ -79,11 +82,15 @@ impl ComposeCpCommand { impl DockerCommand for ComposeCpCommand { type Output = ComposeCpResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -106,16 +113,16 @@ impl DockerCommand for ComposeCpCommand { } impl ComposeCommand for ComposeCpCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "cp" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "cp" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -142,19 +149,19 @@ impl ComposeCommand for ComposeCpCommand { } impl ComposeCpResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the source path used + /// Gets the source path used. #[must_use] pub fn source(&self) -> &str { &self.source } - /// Get the destination path used + /// Gets the destination path used. #[must_use] pub fn destination(&self) -> &str { &self.destination diff --git a/src/command/compose/create.rs b/src/command/compose/create.rs index d50e8db8..615b4c66 100644 --- a/src/command/compose/create.rs +++ b/src/command/compose/create.rs @@ -1,43 +1,47 @@ //! Docker Compose create command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose create command builder +/// Docker Compose create command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for create command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for create command pub struct ComposeCreateCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Build images before creating containers + /// Builds images before creating containers. pub build: bool, - /// Don't build images, even if missing + /// Doesn't build images, even if missing. pub no_build: bool, - /// Force recreate containers + /// Force recreates containers. pub force_recreate: bool, - /// Don't recreate containers if they exist + /// Doesn't recreate containers if they exist. pub no_recreate: bool, - /// Pull images before creating + /// Image pulling policy. pub pull: Option, - /// Remove orphaned containers + /// Removes orphaned containers. pub remove_orphans: bool, - /// Services to create (empty for all) + /// Services to create (empty for all). pub services: Vec, } -/// Pull policy for images -#[derive(Debug, Clone, Copy)] +/// Image pulling policy. +#[derive(Debug, Default, Clone, Copy)] pub enum PullPolicy { - /// Always pull images + /// Always pulls images. Always, - /// Never pull images + /// Never pulls images. Never, - /// Pull missing images (default) + /// Pulls missing images (default). + #[default] Missing, - /// Pull images if local is older + /// Pulls images if local is older. Build, } @@ -52,21 +56,21 @@ impl std::fmt::Display for PullPolicy { } } -/// Result from compose create command +/// Result from compose create command. #[derive(Debug, Clone)] pub struct ComposeCreateResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were created + /// Services that were created. pub services: Vec, } impl ComposeCreateCommand { - /// Create a new compose create command + /// Creates a new compose create command. #[must_use] pub fn new() -> Self { Self { @@ -82,56 +86,56 @@ impl ComposeCreateCommand { } } - /// Build images before creating containers + /// Builds images before creating containers. #[must_use] pub fn build(mut self) -> Self { self.build = true; self } - /// Don't build images, even if missing + /// Doesn't build images, even if missing. #[must_use] pub fn no_build(mut self) -> Self { self.no_build = true; self } - /// Force recreate containers + /// Force recreates containers. #[must_use] pub fn force_recreate(mut self) -> Self { self.force_recreate = true; self } - /// Don't recreate containers if they exist + /// Doesn't recreate containers if they exist. #[must_use] pub fn no_recreate(mut self) -> Self { self.no_recreate = true; self } - /// Set pull policy + /// Sets pull policy. #[must_use] pub fn pull(mut self, policy: PullPolicy) -> Self { self.pull = Some(policy); self } - /// Remove orphaned containers + /// Removes orphaned containers. #[must_use] pub fn remove_orphans(mut self) -> Self { self.remove_orphans = true; self } - /// Add a service to create + /// Adds a service to create. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to create + /// Adds multiple services to create. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -153,6 +157,10 @@ impl Default for ComposeCreateCommand { impl DockerCommand for ComposeCreateCommand { type Output = ComposeCreateResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -162,7 +170,6 @@ impl DockerCommand for ComposeCreateCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -180,6 +187,10 @@ impl DockerCommand for ComposeCreateCommand { } impl ComposeCommand for ComposeCreateCommand { + fn subcommand_name() -> &'static str { + "create" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -188,10 +199,6 @@ impl ComposeCommand for ComposeCreateCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "create" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -215,13 +222,13 @@ impl ComposeCommand for ComposeCreateCommand { args.push("--remove-orphans".to_string()); } - // Add pull policy + // add pull policy if let Some(pull) = self.pull { args.push("--pull".to_string()); args.push(pull.to_string()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -229,13 +236,13 @@ impl ComposeCommand for ComposeCreateCommand { } impl ComposeCreateResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were created + /// Gets the services that were created. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/down.rs b/src/command/compose/down.rs index 18ff9e50..22fc9ca7 100644 --- a/src/command/compose/down.rs +++ b/src/command/compose/down.rs @@ -1,35 +1,38 @@ //! Docker Compose down command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::time::Duration; -/// Docker Compose down command builder +/// Docker Compose down command builder. #[derive(Debug, Clone)] pub struct ComposeDownCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Remove images + /// Removes images. pub remove_images: Option, - /// Remove named volumes + /// Removes named volumes. pub volumes: bool, - /// Remove orphan containers + /// Removes orphan containers. pub remove_orphans: bool, - /// Timeout for container shutdown + /// Timeout for container shutdown. pub timeout: Option, - /// Services to stop (empty for all) + /// Services to stop (empty for all). pub services: Vec, } -/// Image removal options for compose down +/// Image removal options for compose down. #[derive(Debug, Clone, Copy)] pub enum RemoveImages { - /// Remove all images used by services + /// Removes all images used by services. All, - /// Remove only images that don't have a custom tag + /// Removes only images that don't have a custom tag. Local, } @@ -42,23 +45,23 @@ impl std::fmt::Display for RemoveImages { } } -/// Result from compose down command +/// Result from compose down command. #[derive(Debug, Clone)] pub struct ComposeDownResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Whether volumes were removed + /// Whether volumes were removed. pub removed_volumes: bool, - /// Whether images were removed + /// Whether images were removed. pub removed_images: bool, } impl ComposeDownCommand { - /// Create a new compose down command + /// Creates a new compose down command. #[must_use] pub fn new() -> Self { Self { @@ -72,42 +75,42 @@ impl ComposeDownCommand { } } - /// Remove images (all or local) + /// Removes images (all or local). #[must_use] pub fn remove_images(mut self, policy: RemoveImages) -> Self { self.remove_images = Some(policy); self } - /// Remove named volumes declared in the volumes section + /// Removes named volumes declared in the volumes section. #[must_use] pub fn volumes(mut self) -> Self { self.volumes = true; self } - /// Remove containers for services not defined in the compose file + /// Removes containers for services not defined in the compose file. #[must_use] pub fn remove_orphans(mut self) -> Self { self.remove_orphans = true; self } - /// Set timeout for container shutdown + /// Sets timeout for container shutdown. #[must_use] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); self } - /// Add a service to stop + /// Adds a service to stop. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services + /// Adds multiple services. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -129,6 +132,10 @@ impl Default for ComposeDownCommand { impl DockerCommand for ComposeDownCommand { type Output = ComposeDownResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -138,7 +145,6 @@ impl DockerCommand for ComposeDownCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -157,6 +163,10 @@ impl DockerCommand for ComposeDownCommand { } impl ComposeCommand for ComposeDownCommand { + fn subcommand_name() -> &'static str { + "down" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -165,10 +175,6 @@ impl ComposeCommand for ComposeDownCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "down" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -190,7 +196,7 @@ impl ComposeCommand for ComposeDownCommand { args.push(timeout.as_secs().to_string()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -198,19 +204,19 @@ impl ComposeCommand for ComposeDownCommand { } impl ComposeDownResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Check if volumes were removed + /// Checks if volumes were removed. #[must_use] pub fn volumes_removed(&self) -> bool { self.removed_volumes } - /// Check if images were removed + /// Checks if images were removed. #[must_use] pub fn images_removed(&self) -> bool { self.removed_images diff --git a/src/command/compose/events.rs b/src/command/compose/events.rs index 428068ce..dfc3dcd4 100644 --- a/src/command/compose/events.rs +++ b/src/command/compose/events.rs @@ -1,62 +1,65 @@ //! Docker Compose events command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use serde::Deserialize; -/// Docker Compose events command builder +/// Docker Compose events command builder. #[derive(Debug, Clone)] pub struct ComposeEventsCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Output format as JSON + /// Output format as JSON. pub json: bool, - /// Start timestamp + /// Start timestamp. pub since: Option, - /// End timestamp + /// End timestamp. pub until: Option, - /// Services to get events for (empty for all) + /// Services to get events for (empty for all). pub services: Vec, } -/// Event from Docker Compose +/// Event from Docker Compose. #[derive(Debug, Clone, Deserialize)] pub struct ComposeEvent { - /// Time of the event + /// Time of the event. pub time: String, - /// Type of the event + /// Type of the event. #[serde(rename = "type")] pub event_type: String, - /// Action that occurred + /// Action that occurred. pub action: String, - /// Service name + /// Service name. pub service: Option, - /// Container ID + /// Container ID. pub container: Option, - /// Additional attributes + /// Additional attributes. pub attributes: Option, } -/// Result from compose events command +/// Result from compose events command. #[derive(Debug, Clone)] pub struct ComposeEventsResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Parsed events (if JSON format was used) + /// Parsed events (if JSON format was used). pub events: Vec, - /// Services that were monitored + /// Services that were monitored. pub services: Vec, } impl ComposeEventsCommand { - /// Create a new compose events command + /// Creates a new compose events command. #[must_use] pub fn new() -> Self { Self { @@ -69,35 +72,35 @@ impl ComposeEventsCommand { } } - /// Output events in JSON format + /// Outputs events in JSON format. #[must_use] pub fn json(mut self) -> Self { self.json = true; self } - /// Set start timestamp for events + /// Sets start timestamp for events. #[must_use] pub fn since(mut self, timestamp: impl Into) -> Self { self.since = Some(timestamp.into()); self } - /// Set end timestamp for events + /// Sets end timestamp for events. #[must_use] pub fn until(mut self, timestamp: impl Into) -> Self { self.until = Some(timestamp.into()); self } - /// Add a service to monitor events for + /// Adds a service to monitor events for. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to monitor events for + /// Adds multiple services to monitor events for. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -117,18 +120,20 @@ impl Default for ComposeEventsCommand { #[async_trait] impl DockerCommand for ComposeEventsCommand { + fn command_name() -> &'static str { + ::command_name() + } type Output = ComposeEventsResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -164,16 +169,16 @@ impl DockerCommand for ComposeEventsCommand { } impl ComposeCommand for ComposeEventsCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "events" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "events" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -193,7 +198,7 @@ impl ComposeCommand for ComposeEventsCommand { args.push(until.clone()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -201,25 +206,25 @@ impl ComposeCommand for ComposeEventsCommand { } impl ComposeEventsResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get parsed events (if JSON format was used) + /// Gets parsed events (if JSON format was used). #[must_use] pub fn events(&self) -> &[ComposeEvent] { &self.events } - /// Get the services that were monitored + /// Gets the services that were monitored. #[must_use] pub fn services(&self) -> &[String] { &self.services } - /// Get events for a specific service + /// Gets events for a specific service. #[must_use] pub fn events_for_service(&self, service: &str) -> Vec<&ComposeEvent> { self.events diff --git a/src/command/compose/exec.rs b/src/command/compose/exec.rs index 2212c7e6..cf5fd097 100644 --- a/src/command/compose/exec.rs +++ b/src/command/compose/exec.rs @@ -1,59 +1,62 @@ //! Docker Compose exec command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::collections::HashMap; -/// Docker Compose exec command builder +/// Docker Compose exec command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for exec command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for exec command pub struct ComposeExecCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Service to execute command in + /// Service to execute command in. pub service: String, - /// Command and arguments to execute + /// Command and arguments to execute. pub command: Vec, - /// Run in detached mode + /// Runs in detached mode. pub detach: bool, - /// Disable pseudo-TTY allocation + /// Disables pseudo-TTY allocation. pub no_tty: bool, - /// Keep STDIN open even if not attached + /// Keeps STDIN open even if not attached. pub interactive: bool, - /// Run as specified user + /// Runs as specified user. pub user: Option, - /// Working directory inside the container + /// Working directory inside the container. pub workdir: Option, - /// Set environment variables + /// Sets environment variables. pub env: HashMap, - /// Container index (if service has multiple instances) + /// Container index (if service has multiple instances). pub index: Option, - /// Use privileged mode + /// Uses privileged mode. pub privileged: bool, } -/// Result from compose exec command +/// Result from compose exec command. #[derive(Debug, Clone)] pub struct ComposeExecResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Exit code from the command + /// Exit code from the command. pub exit_code: i32, - /// Service that the command was executed in + /// Service that the command was executed in. pub service: String, - /// Whether the command was run in detached mode + /// Whether the command was run in detached mode. pub detached: bool, } impl ComposeExecCommand { - /// Create a new compose exec command + /// Creates a new compose exec command. #[must_use] pub fn new(service: impl Into) -> Self { Self { @@ -72,7 +75,7 @@ impl ComposeExecCommand { } } - /// Set the command to execute + /// Sets the command to execute. #[must_use] pub fn cmd(mut self, command: I) -> Self where @@ -83,14 +86,14 @@ impl ComposeExecCommand { self } - /// Add a command argument + /// Adds a command argument. #[must_use] pub fn arg(mut self, arg: impl Into) -> Self { self.command.push(arg.into()); self } - /// Add multiple arguments + /// Adds multiple arguments. #[must_use] pub fn args(mut self, args: I) -> Self where @@ -101,63 +104,63 @@ impl ComposeExecCommand { self } - /// Run in detached mode + /// Runs in detached mode. #[must_use] pub fn detach(mut self) -> Self { self.detach = true; self } - /// Disable pseudo-TTY allocation + /// Disables pseudo-TTY allocation. #[must_use] pub fn no_tty(mut self) -> Self { self.no_tty = true; self } - /// Keep STDIN open even if not attached + /// Keeps STDIN open even if not attached. #[must_use] pub fn interactive(mut self) -> Self { self.interactive = true; self } - /// Run as specified user + /// Runs as specified user. #[must_use] pub fn user(mut self, user: impl Into) -> Self { self.user = Some(user.into()); self } - /// Set working directory inside the container + /// Sets working directory inside the container. #[must_use] pub fn workdir(mut self, workdir: impl Into) -> Self { self.workdir = Some(workdir.into()); self } - /// Set an environment variable + /// Sets an environment variable. #[must_use] pub fn env(mut self, key: impl Into, value: impl Into) -> Self { self.env.insert(key.into(), value.into()); self } - /// Set multiple environment variables + /// Sets multiple environment variables. #[must_use] pub fn envs(mut self, env_vars: HashMap) -> Self { self.env.extend(env_vars); self } - /// Set container index (for services with multiple instances) + /// Sets container index (for services with multiple instances). #[must_use] pub fn index(mut self, index: u32) -> Self { self.index = Some(index); self } - /// Use privileged mode + /// Uses privileged mode. #[must_use] pub fn privileged(mut self) -> Self { self.privileged = true; @@ -169,6 +172,10 @@ impl ComposeExecCommand { impl DockerCommand for ComposeExecCommand { type Output = ComposeExecResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -178,7 +185,6 @@ impl DockerCommand for ComposeExecCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -198,6 +204,10 @@ impl DockerCommand for ComposeExecCommand { } impl ComposeCommand for ComposeExecCommand { + fn subcommand_name() -> &'static str { + "exec" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -206,10 +216,6 @@ impl ComposeCommand for ComposeExecCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "exec" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -225,25 +231,25 @@ impl ComposeCommand for ComposeExecCommand { args.push("--interactive".to_string()); } - // Add user + // add user if let Some(ref user) = self.user { args.push("--user".to_string()); args.push(user.clone()); } - // Add working directory + // add working directory if let Some(ref workdir) = self.workdir { args.push("--workdir".to_string()); args.push(workdir.clone()); } - // Add environment variables + // add environment variables for (key, value) in &self.env { args.push("--env".to_string()); args.push(format!("{key}={value}")); } - // Add container index + // add container index if let Some(index) = self.index { args.push("--index".to_string()); args.push(index.to_string()); @@ -253,10 +259,10 @@ impl ComposeCommand for ComposeExecCommand { args.push("--privileged".to_string()); } - // Add service name + // add service name args.push(self.service.clone()); - // Add command and arguments + // add command and arguments args.extend(self.command.clone()); args @@ -264,25 +270,25 @@ impl ComposeCommand for ComposeExecCommand { } impl ComposeExecResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the exit code from the command + /// Gets the exit code from the command. #[must_use] pub fn exit_code(&self) -> i32 { self.exit_code } - /// Get the service that the command was executed in + /// Gets the service that the command was executed in. #[must_use] pub fn service(&self) -> &str { &self.service } - /// Check if the command was run in detached mode + /// Checks if the command was run in detached mode. #[must_use] pub fn is_detached(&self) -> bool { self.detached diff --git a/src/command/compose/images.rs b/src/command/compose/images.rs index d3bcf11c..551c8a8a 100644 --- a/src/command/compose/images.rs +++ b/src/command/compose/images.rs @@ -1,31 +1,35 @@ //! Docker Compose images command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use serde::Deserialize; -/// Docker Compose images command builder +/// Docker Compose images command builder. #[derive(Debug, Clone)] pub struct ComposeImagesCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Output format + /// Output format. pub format: Option, - /// Only display image IDs + /// Only displays image IDs. pub quiet: bool, - /// Services to list images for (empty for all) + /// Services to list images for (empty for all). pub services: Vec, } -/// Images output format -#[derive(Debug, Clone, Copy)] +/// Images output format. +#[derive(Debug, Default, Clone, Copy)] pub enum ImagesFormat { - /// Table format (default) + /// Table format (default). + #[default] Table, - /// JSON format + /// JSON format. Json, } @@ -38,39 +42,39 @@ impl std::fmt::Display for ImagesFormat { } } -/// Image information from JSON output +/// Image information from JSON output. #[derive(Debug, Clone, Deserialize)] pub struct ImageInfo { - /// Container name + /// Container name. pub container: String, - /// Repository + /// Repository. pub repository: String, - /// Tag + /// Tag. pub tag: String, - /// Image ID + /// Image ID. #[serde(rename = "ID")] pub id: String, - /// Size + /// Size. pub size: String, } -/// Result from compose images command +/// Result from compose images command. #[derive(Debug, Clone)] pub struct ComposeImagesResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Parsed image information (if JSON format) + /// Parsed image information (if JSON format). pub images: Vec, - /// Services that were queried + /// Services that were queried. pub services: Vec, } impl ComposeImagesCommand { - /// Create a new compose images command + /// Creates a new compose images command. #[must_use] pub fn new() -> Self { Self { @@ -82,42 +86,42 @@ impl ComposeImagesCommand { } } - /// Set output format + /// Sets output format. #[must_use] pub fn format(mut self, format: ImagesFormat) -> Self { self.format = Some(format); self } - /// Set output format to JSON + /// Sets output format to JSON. #[must_use] pub fn format_json(mut self) -> Self { self.format = Some(ImagesFormat::Json); self } - /// Set output format to table + /// Sets output format to table. #[must_use] pub fn format_table(mut self) -> Self { self.format = Some(ImagesFormat::Table); self } - /// Only display image IDs + /// Only displays image IDs. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; self } - /// Add a service to list images for + /// Adds a service to list images for. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to list images for + /// Adds multiple services to list images for. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -139,11 +143,15 @@ impl Default for ComposeImagesCommand { impl DockerCommand for ComposeImagesCommand { type Output = ComposeImagesResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -172,16 +180,16 @@ impl DockerCommand for ComposeImagesCommand { } impl ComposeCommand for ComposeImagesCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "images" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "images" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -202,19 +210,19 @@ impl ComposeCommand for ComposeImagesCommand { } impl ComposeImagesResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get parsed image information + /// Gets parsed image information. #[must_use] pub fn images(&self) -> &[ImageInfo] { &self.images } - /// Get the services that were queried + /// Gets the services that were queried. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/kill.rs b/src/command/compose/kill.rs index 2a64552f..c20d5770 100644 --- a/src/command/compose/kill.rs +++ b/src/command/compose/kill.rs @@ -1,37 +1,40 @@ //! Docker Compose kill command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose kill command builder +/// Docker Compose kill command builder. #[derive(Debug, Clone)] pub struct ComposeKillCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to kill (empty for all) + /// Services to kill (empty for all). pub services: Vec, - /// Signal to send to containers + /// Signal to send to containers. pub signal: Option, } -/// Result from compose kill command +/// Result from compose kill command. #[derive(Debug, Clone)] pub struct ComposeKillResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were killed + /// Services that were killed. pub services: Vec, } impl ComposeKillCommand { - /// Create a new compose kill command + /// Creates a new compose kill command. #[must_use] pub fn new() -> Self { Self { @@ -42,14 +45,14 @@ impl ComposeKillCommand { } } - /// Add a service to kill + /// Adds a service to kill. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to kill + /// Adds multiple services to kill. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -60,7 +63,7 @@ impl ComposeKillCommand { self } - /// Set signal to send to containers + /// Sets signal to send to containers. #[must_use] pub fn signal(mut self, signal: impl Into) -> Self { self.signal = Some(signal.into()); @@ -78,6 +81,10 @@ impl Default for ComposeKillCommand { impl DockerCommand for ComposeKillCommand { type Output = ComposeKillResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -87,7 +94,6 @@ impl DockerCommand for ComposeKillCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -105,6 +111,10 @@ impl DockerCommand for ComposeKillCommand { } impl ComposeCommand for ComposeKillCommand { + fn subcommand_name() -> &'static str { + "kill" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -113,10 +123,6 @@ impl ComposeCommand for ComposeKillCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "kill" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -125,7 +131,7 @@ impl ComposeCommand for ComposeKillCommand { args.push(signal.clone()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -133,13 +139,13 @@ impl ComposeCommand for ComposeKillCommand { } impl ComposeKillResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were killed + /// Gets the services that were killed. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/logs.rs b/src/command/compose/logs.rs index cb75c3f2..1ddbbd14 100644 --- a/src/command/compose/logs.rs +++ b/src/command/compose/logs.rs @@ -1,50 +1,53 @@ //! Docker Compose logs command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose logs command builder +/// Docker Compose logs command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for logs command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for logs command pub struct ComposeLogsCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to show logs for (empty for all) + /// Services to show logs for (empty for all). pub services: Vec, - /// Follow log output + /// Follows log output. pub follow: bool, - /// Show timestamps + /// Shows timestamps. pub timestamps: bool, - /// Number of lines to show from the end + /// Number of lines to show from the end. pub tail: Option, - /// Show logs since timestamp + /// Shows logs since timestamp. pub since: Option, - /// Show logs until timestamp + /// Shows logs until timestamp. pub until: Option, - /// Don't print prefix + /// Doesn't print prefix. pub no_log_prefix: bool, - /// Don't use colors + /// Doesn't use colors. pub no_color: bool, } -/// Result from compose logs command +/// Result from compose logs command. #[derive(Debug, Clone)] pub struct ComposeLogsResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services logs were fetched for + /// Services logs were fetched for. pub services: Vec, } impl ComposeLogsCommand { - /// Create a new compose logs command + /// Creates a new compose logs command. #[must_use] pub fn new() -> Self { Self { @@ -61,14 +64,14 @@ impl ComposeLogsCommand { } } - /// Add a service to show logs for + /// Adds a service to show logs for. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services + /// Adds multiple services. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -79,49 +82,49 @@ impl ComposeLogsCommand { self } - /// Follow log output + /// Follows log output. #[must_use] pub fn follow(mut self) -> Self { self.follow = true; self } - /// Show timestamps + /// Shows timestamps. #[must_use] pub fn timestamps(mut self) -> Self { self.timestamps = true; self } - /// Number of lines to show from the end + /// Number of lines to show from the end. #[must_use] pub fn tail(mut self, lines: impl Into) -> Self { self.tail = Some(lines.into()); self } - /// Show logs since timestamp + /// Shows logs since timestamp. #[must_use] pub fn since(mut self, timestamp: impl Into) -> Self { self.since = Some(timestamp.into()); self } - /// Show logs until timestamp + /// Shows logs until timestamp. #[must_use] pub fn until(mut self, timestamp: impl Into) -> Self { self.until = Some(timestamp.into()); self } - /// Don't print prefix + /// Doesn't print prefix. #[must_use] pub fn no_log_prefix(mut self) -> Self { self.no_log_prefix = true; self } - /// Don't use colors + /// Doesn't use colors. #[must_use] pub fn no_color(mut self) -> Self { self.no_color = true; @@ -139,6 +142,10 @@ impl Default for ComposeLogsCommand { impl DockerCommand for ComposeLogsCommand { type Output = ComposeLogsResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -148,7 +155,6 @@ impl DockerCommand for ComposeLogsCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -166,6 +172,10 @@ impl DockerCommand for ComposeLogsCommand { } impl ComposeCommand for ComposeLogsCommand { + fn subcommand_name() -> &'static str { + "logs" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -174,10 +184,6 @@ impl ComposeCommand for ComposeLogsCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "logs" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -212,7 +218,7 @@ impl ComposeCommand for ComposeLogsCommand { args.push("--no-color".to_string()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -220,13 +226,13 @@ impl ComposeCommand for ComposeLogsCommand { } impl ComposeLogsResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services logs were fetched for + /// Gets the services logs were fetched for. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/ls.rs b/src/command/compose/ls.rs index 020eb1c8..cedf0b72 100644 --- a/src/command/compose/ls.rs +++ b/src/command/compose/ls.rs @@ -1,40 +1,44 @@ //! Docker Compose ls command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use serde::Deserialize; -/// Docker Compose ls command +/// Docker Compose ls command. /// -/// List running compose projects. +/// Lists running compose projects. #[derive(Debug, Clone, Default)] pub struct ComposeLsCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Show all projects (including stopped) + /// Shows all projects (including stopped). pub all: bool, - /// Filter by name + /// Filters by name. pub filter: Option, - /// Format output (table, json) + /// Formats output (table, json). pub format: Option, - /// Only display project names + /// Only displays project names. pub quiet: bool, } -/// Ls output format -#[derive(Debug, Clone, Copy)] +/// Ls output format. +#[derive(Debug, Default, Clone, Copy)] pub enum LsFormat { - /// Table format (default) + /// Table format (default). + #[default] Table, - /// JSON format + /// JSON format. Json, } impl LsFormat { - /// Convert to command line argument + /// Converts to command line argument. #[must_use] pub fn as_arg(&self) -> &str { match self { @@ -44,33 +48,33 @@ impl LsFormat { } } -/// Compose project information +/// Compose project information. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct ComposeProject { - /// Project name + /// Project name. pub name: String, - /// Status + /// Status. pub status: String, - /// Configuration files + /// Configuration files. #[serde(default)] pub config_files: String, - /// Created timestamp + /// Created timestamp. #[serde(default)] pub created: String, } -/// Result from ls command +/// Result from ls command. #[derive(Debug, Clone)] pub struct LsResult { - /// List of compose projects + /// List of compose projects. pub projects: Vec, - /// Raw output (for non-JSON formats) + /// Raw output (for non-JSON formats). pub raw_output: String, } impl ComposeLsCommand { - /// Create a new ls command + /// Creates a new ls command. #[must_use] pub fn new() -> Self { Self { @@ -80,35 +84,35 @@ impl ComposeLsCommand { } } - /// Show all projects + /// Shows all projects. #[must_use] pub fn all(mut self) -> Self { self.all = true; self } - /// Filter projects + /// Filters projects. #[must_use] pub fn filter(mut self, filter: impl Into) -> Self { self.filter = Some(filter.into()); self } - /// Set output format + /// Sets output format. #[must_use] pub fn format(mut self, format: LsFormat) -> Self { self.format = Some(format); self } - /// Set output format to JSON + /// Sets output format to JSON. #[must_use] pub fn format_json(mut self) -> Self { self.format = Some(LsFormat::Json); self } - /// Only display project names + /// Only displays project names. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; @@ -120,6 +124,10 @@ impl ComposeLsCommand { impl DockerCommand for ComposeLsCommand { type Output = LsResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -129,7 +137,6 @@ impl DockerCommand for ComposeLsCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -137,7 +144,7 @@ impl DockerCommand for ComposeLsCommand { let args = ::build_command_args(self); let output = self.execute_command(args).await?; - // Parse JSON output if format is JSON + // parses JSON output if format is JSON let projects = if matches!(self.format, Some(LsFormat::Json)) { serde_json::from_str(&output.stdout).unwrap_or_default() } else { @@ -152,6 +159,10 @@ impl DockerCommand for ComposeLsCommand { } impl ComposeCommand for ComposeLsCommand { + fn subcommand_name() -> &'static str { + "ls" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -160,14 +171,10 @@ impl ComposeCommand for ComposeLsCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "ls" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); - // Add flags + // add flags if self.all { args.push("--all".to_string()); } @@ -175,13 +182,13 @@ impl ComposeCommand for ComposeLsCommand { args.push("--quiet".to_string()); } - // Add filter + // add filter if let Some(filter) = &self.filter { args.push("--filter".to_string()); args.push(filter.clone()); } - // Add format + // add format if let Some(format) = &self.format { args.push("--format".to_string()); args.push(format.as_arg().to_string()); @@ -192,19 +199,19 @@ impl ComposeCommand for ComposeLsCommand { } impl LsResult { - /// Get project names + /// Gets project names. #[must_use] pub fn project_names(&self) -> Vec { self.projects.iter().map(|p| p.name.clone()).collect() } - /// Check if a project exists + /// Checks if a project exists. #[must_use] pub fn has_project(&self, name: &str) -> bool { self.projects.iter().any(|p| p.name == name) } - /// Get running projects + /// Gets running projects. #[must_use] pub fn running_projects(&self) -> Vec<&ComposeProject> { self.projects @@ -222,7 +229,7 @@ mod tests { fn test_ls_command_basic() { let cmd = ComposeLsCommand::new(); let _args = cmd.build_subcommand_args(); - // No specific args for basic command + // no specific args for basic command let full_args = ComposeCommand::build_command_args(&cmd); assert_eq!(full_args[0], "compose"); diff --git a/src/command/compose/pause.rs b/src/command/compose/pause.rs index 91a09464..031dc785 100644 --- a/src/command/compose/pause.rs +++ b/src/command/compose/pause.rs @@ -1,35 +1,38 @@ //! Docker Compose pause command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose pause command builder +/// Docker Compose pause command builder. #[derive(Debug, Clone)] pub struct ComposePauseCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to pause (empty for all) + /// Services to pause (empty for all). pub services: Vec, } -/// Result from compose pause command +/// Result from compose pause command. #[derive(Debug, Clone)] pub struct ComposePauseResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were paused + /// Services that were paused. pub services: Vec, } impl ComposePauseCommand { - /// Create a new compose pause command + /// Creates a new compose pause command. #[must_use] pub fn new() -> Self { Self { @@ -39,14 +42,14 @@ impl ComposePauseCommand { } } - /// Add a service to pause + /// Adds a service to pause. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to pause + /// Adds multiple services to pause. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -68,6 +71,10 @@ impl Default for ComposePauseCommand { impl DockerCommand for ComposePauseCommand { type Output = ComposePauseResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -77,7 +84,6 @@ impl DockerCommand for ComposePauseCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -95,6 +101,10 @@ impl DockerCommand for ComposePauseCommand { } impl ComposeCommand for ComposePauseCommand { + fn subcommand_name() -> &'static str { + "pause" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -103,24 +113,19 @@ impl ComposeCommand for ComposePauseCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "pause" - } - fn build_subcommand_args(&self) -> Vec { - // Pause command just takes service names self.services.clone() } } impl ComposePauseResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were paused + /// Gets the services that were paused. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/port.rs b/src/command/compose/port.rs index f1b51b92..e6075144 100644 --- a/src/command/compose/port.rs +++ b/src/command/compose/port.rs @@ -1,43 +1,46 @@ //! Docker Compose port command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose port command builder +/// Docker Compose port command builder. #[derive(Debug, Clone)] pub struct ComposePortCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Service name + /// Service name. pub service: String, - /// Private port to query + /// Private port to query. pub private_port: Option, - /// Protocol (tcp/udp) + /// Protocol (tcp/udp). pub protocol: Option, - /// Index of container if service has multiple instances + /// Index of container if service has multiple instances. pub index: Option, } -/// Result from compose port command +/// Result from compose port command. #[derive(Debug, Clone)] pub struct ComposePortResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Service that was queried + /// Service that was queried. pub service: String, - /// Port mappings found + /// Port mappings found. pub port_mappings: Vec, } impl ComposePortCommand { - /// Create a new compose port command + /// Creates a new compose port command. #[must_use] pub fn new(service: impl Into) -> Self { Self { @@ -50,21 +53,21 @@ impl ComposePortCommand { } } - /// Set private port to query + /// Sets private port to query. #[must_use] pub fn private_port(mut self, port: u16) -> Self { self.private_port = Some(port); self } - /// Set protocol (tcp or udp) + /// Sets protocol (tcp or udp). #[must_use] pub fn protocol(mut self, protocol: impl Into) -> Self { self.protocol = Some(protocol.into()); self } - /// Set container index if service has multiple instances + /// Sets container index if service has multiple instances. #[must_use] pub fn index(mut self, index: u16) -> Self { self.index = Some(index); @@ -74,13 +77,16 @@ impl ComposePortCommand { #[async_trait] impl DockerCommand for ComposePortCommand { + fn command_name() -> &'static str { + ::command_name() + } type Output = ComposePortResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -110,16 +116,16 @@ impl DockerCommand for ComposePortCommand { } impl ComposeCommand for ComposePortCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "port" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "port" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -146,19 +152,19 @@ impl ComposeCommand for ComposePortCommand { } impl ComposePortResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the service that was queried + /// Gets the service that was queried. #[must_use] pub fn service(&self) -> &str { &self.service } - /// Get port mappings + /// Gets port mappings. #[must_use] pub fn port_mappings(&self) -> &[String] { &self.port_mappings diff --git a/src/command/compose/ps.rs b/src/command/compose/ps.rs index a0fdf202..1fec3c62 100644 --- a/src/command/compose/ps.rs +++ b/src/command/compose/ps.rs @@ -1,43 +1,46 @@ //! Docker Compose ps command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use serde::{Deserialize, Serialize}; -/// Docker Compose ps command builder +/// Docker Compose ps command builder. #[derive(Debug, Clone)] pub struct ComposePsCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to list (empty for all) + /// Services to list (empty for all). pub services: Vec, - /// Show all containers (including stopped) + /// Shows all containers (including stopped). pub all: bool, - /// Only display container IDs + /// Only displays container IDs. pub quiet: bool, - /// Show services + /// Shows services. pub show_services: bool, - /// Filter containers + /// Filters containers. pub filter: Vec, - /// Output format + /// Output format. pub format: Option, - /// Only show running containers + /// Only shows running containers. pub status: Option>, } -/// Container status filter +/// Container status filter. #[derive(Debug, Clone, Copy)] pub enum ContainerStatus { - /// Paused containers + /// Paused containers. Paused, - /// Restarting containers + /// Restarting containers. Restarting, - /// Running containers + /// Running containers. Running, - /// Stopped containers + /// Stopped containers. Stopped, } @@ -52,61 +55,61 @@ impl std::fmt::Display for ContainerStatus { } } -/// Container information from compose ps +/// Container information from compose ps. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComposeContainerInfo { - /// Container ID + /// Container ID. #[serde(rename = "ID")] pub id: String, - /// Container name + /// Container name. #[serde(rename = "Name")] pub name: String, - /// Service name + /// Service name. #[serde(rename = "Service")] pub service: String, - /// Container state + /// Container state. #[serde(rename = "State")] pub state: String, - /// Health status + /// Health status. #[serde(rename = "Health")] pub health: Option, - /// Exit code + /// Exit code. #[serde(rename = "ExitCode")] pub exit_code: Option, - /// Published ports + /// Published ports. #[serde(rename = "Publishers")] pub publishers: Option>, } -/// Port publishing information +/// Port publishing information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PortPublisher { - /// Target port + /// Target port. #[serde(rename = "TargetPort")] pub target_port: u16, - /// Published port + /// Published port. #[serde(rename = "PublishedPort")] pub published_port: Option, - /// Protocol + /// Protocol. #[serde(rename = "Protocol")] pub protocol: String, } -/// Result from compose ps command +/// Result from compose ps command. #[derive(Debug, Clone)] pub struct ComposePsResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Parsed container information (if JSON format) + /// Parsed container information (if JSON format). pub containers: Vec, } impl ComposePsCommand { - /// Create a new compose ps command + /// Creates a new compose ps command. #[must_use] pub fn new() -> Self { Self { @@ -122,63 +125,63 @@ impl ComposePsCommand { } } - /// Add a service to list + /// Adds a service to list. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Show all containers (default shows only running) + /// Shows all containers (default shows only running). #[must_use] pub fn all(mut self) -> Self { self.all = true; self } - /// Only display container IDs + /// Only displays container IDs. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; self } - /// Display services + /// Displays services. #[must_use] pub fn services(mut self) -> Self { self.show_services = true; self } - /// Add a filter + /// Adds a filter. #[must_use] pub fn filter(mut self, filter: impl Into) -> Self { self.filter.push(filter.into()); self } - /// Set output format + /// Sets output format. #[must_use] pub fn format(mut self, format: impl Into) -> Self { self.format = Some(format.into()); self } - /// Filter by status + /// Filters by status. #[must_use] pub fn status(mut self, status: ContainerStatus) -> Self { self.status.get_or_insert_with(Vec::new).push(status); self } - /// Use JSON output format + /// Uses JSON output format. #[must_use] pub fn json(mut self) -> Self { self.format = Some("json".to_string()); self } - /// Parse JSON output into container info + /// Parses JSON output into container info. fn parse_json_output(stdout: &str) -> Vec { stdout .lines() @@ -198,6 +201,10 @@ impl Default for ComposePsCommand { impl DockerCommand for ComposePsCommand { type Output = ComposePsResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -207,7 +214,6 @@ impl DockerCommand for ComposePsCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -215,7 +221,7 @@ impl DockerCommand for ComposePsCommand { let args = ::build_command_args(self); let output = self.execute_command(args).await?; - // Parse JSON output if format is json + // parses JSON output if format is json let containers = if self.format.as_deref() == Some("json") { Self::parse_json_output(&output.stdout) } else { @@ -232,6 +238,10 @@ impl DockerCommand for ComposePsCommand { } impl ComposeCommand for ComposePsCommand { + fn subcommand_name() -> &'static str { + "ps" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -240,10 +250,6 @@ impl ComposeCommand for ComposePsCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "ps" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -284,26 +290,26 @@ impl ComposeCommand for ComposePsCommand { } impl ComposePsResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get container information + /// Gets container information. #[must_use] pub fn containers(&self) -> &[ComposeContainerInfo] { &self.containers } - /// Get container IDs from output + /// Gets container IDs from output. #[must_use] pub fn container_ids(&self) -> Vec { if self.containers.is_empty() { - // Parse from text output if not JSON + // parses from text output if not JSON self.stdout .lines() - .skip(1) // Skip header + .skip(1) // skips header .filter_map(|line| line.split_whitespace().next()) .map(String::from) .collect() @@ -312,7 +318,7 @@ impl ComposePsResult { } } - /// Get stdout lines + /// Gets stdout lines. #[must_use] pub fn stdout_lines(&self) -> Vec<&str> { self.stdout.lines().collect() diff --git a/src/command/compose/push.rs b/src/command/compose/push.rs index 0b1af1e8..e8bd76a1 100644 --- a/src/command/compose/push.rs +++ b/src/command/compose/push.rs @@ -1,41 +1,44 @@ //! Docker Compose push command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose push command builder +/// Docker Compose push command builder. #[derive(Debug, Clone)] pub struct ComposePushCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to push images for (empty for all) + /// Services to push images for (empty for all). pub services: Vec, - /// Ignore build failures + /// Ignore build failures. pub ignore_build_failures: bool, - /// Include dependencies + /// Include dependencies. pub include_deps: bool, - /// Quiet mode + /// Quiet mode. pub quiet: bool, } -/// Result from compose push command +/// Result from compose push command. #[derive(Debug, Clone)] pub struct ComposePushResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were pushed + /// Services that were pushed. pub services: Vec, } impl ComposePushCommand { - /// Create a new compose push command + /// Creates a new compose push command. #[must_use] pub fn new() -> Self { Self { @@ -48,14 +51,14 @@ impl ComposePushCommand { } } - /// Add a service to push + /// Adds a service to push. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to push + /// Adds multiple services to push. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -66,21 +69,21 @@ impl ComposePushCommand { self } - /// Ignore build failures + /// Ignores build failures. #[must_use] pub fn ignore_build_failures(mut self) -> Self { self.ignore_build_failures = true; self } - /// Include dependencies + /// Includes dependencies. #[must_use] pub fn include_deps(mut self) -> Self { self.include_deps = true; self } - /// Enable quiet mode + /// Enables quiet mode. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; @@ -98,11 +101,15 @@ impl Default for ComposePushCommand { impl DockerCommand for ComposePushCommand { type Output = ComposePushResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -124,16 +131,16 @@ impl DockerCommand for ComposePushCommand { } impl ComposeCommand for ComposePushCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "push" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "push" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -157,13 +164,13 @@ impl ComposeCommand for ComposePushCommand { } impl ComposePushResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were pushed + /// Gets the services that were pushed. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/restart.rs b/src/command/compose/restart.rs index 1db4c9e8..bc4c5793 100644 --- a/src/command/compose/restart.rs +++ b/src/command/compose/restart.rs @@ -1,38 +1,41 @@ //! Docker Compose restart command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::time::Duration; -/// Docker Compose restart command builder +/// Docker Compose restart command builder. #[derive(Debug, Clone)] pub struct ComposeRestartCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to restart (empty for all) + /// Services to restart (empty for all). pub services: Vec, - /// Timeout for stopping containers before restarting + /// Timeout for stopping containers before restarting. pub timeout: Option, } -/// Result from compose restart command +/// Result from compose restart command. #[derive(Debug, Clone)] pub struct ComposeRestartResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were restarted + /// Services that were restarted. pub services: Vec, } impl ComposeRestartCommand { - /// Create a new compose restart command + /// Creates a new compose restart command. #[must_use] pub fn new() -> Self { Self { @@ -43,14 +46,14 @@ impl ComposeRestartCommand { } } - /// Add a service to restart + /// Adds a service to restart. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to restart + /// Adds multiple services to restart. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -61,7 +64,7 @@ impl ComposeRestartCommand { self } - /// Set the timeout for stopping containers before restarting + /// Sets the timeout for stopping containers before restarting. #[must_use] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); @@ -79,6 +82,10 @@ impl Default for ComposeRestartCommand { impl DockerCommand for ComposeRestartCommand { type Output = ComposeRestartResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -88,7 +95,6 @@ impl DockerCommand for ComposeRestartCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -106,6 +112,10 @@ impl DockerCommand for ComposeRestartCommand { } impl ComposeCommand for ComposeRestartCommand { + fn subcommand_name() -> &'static str { + "restart" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -114,10 +124,6 @@ impl ComposeCommand for ComposeRestartCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "restart" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -126,7 +132,7 @@ impl ComposeCommand for ComposeRestartCommand { args.push(timeout.as_secs().to_string()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -134,13 +140,13 @@ impl ComposeCommand for ComposeRestartCommand { } impl ComposeRestartResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were restarted + /// Gets the services that were restarted. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/rm.rs b/src/command/compose/rm.rs index ce6ada4a..75722d84 100644 --- a/src/command/compose/rm.rs +++ b/src/command/compose/rm.rs @@ -1,44 +1,47 @@ //! Docker Compose rm command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose rm command builder +/// Docker Compose rm command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for rm command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for rm command pub struct ComposeRmCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to remove (empty for all) + /// Services to remove (empty for all). pub services: Vec, - /// Force removal without confirmation + /// Forces removal without confirmation. pub force: bool, - /// Stop containers if running + /// Stops containers if running. pub stop: bool, - /// Remove volumes associated with containers + /// Removes volumes associated with containers. pub volumes: bool, } -/// Result from compose rm command +/// Result from compose rm command. #[derive(Debug, Clone)] pub struct ComposeRmResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were removed + /// Services that were removed. pub services: Vec, - /// Whether volumes were removed + /// Whether volumes were removed. pub volumes_removed: bool, } impl ComposeRmCommand { - /// Create a new compose rm command + /// Creates a new compose rm command. #[must_use] pub fn new() -> Self { Self { @@ -51,14 +54,14 @@ impl ComposeRmCommand { } } - /// Add a service to remove + /// Adds a service to remove. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to remove + /// Adds multiple services to remove. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -69,21 +72,21 @@ impl ComposeRmCommand { self } - /// Force removal without confirmation + /// Forces removal without confirmation. #[must_use] pub fn force(mut self) -> Self { self.force = true; self } - /// Stop containers if running + /// Stops containers if running. #[must_use] pub fn stop(mut self) -> Self { self.stop = true; self } - /// Remove volumes associated with containers + /// Removes volumes associated with containers. #[must_use] pub fn volumes(mut self) -> Self { self.volumes = true; @@ -101,6 +104,10 @@ impl Default for ComposeRmCommand { impl DockerCommand for ComposeRmCommand { type Output = ComposeRmResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -110,7 +117,6 @@ impl DockerCommand for ComposeRmCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -129,6 +135,10 @@ impl DockerCommand for ComposeRmCommand { } impl ComposeCommand for ComposeRmCommand { + fn subcommand_name() -> &'static str { + "rm" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -137,10 +147,6 @@ impl ComposeCommand for ComposeRmCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "rm" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -156,7 +162,7 @@ impl ComposeCommand for ComposeRmCommand { args.push("--volumes".to_string()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -164,19 +170,19 @@ impl ComposeCommand for ComposeRmCommand { } impl ComposeRmResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were removed + /// Gets the services that were removed. #[must_use] pub fn services(&self) -> &[String] { &self.services } - /// Check if volumes were removed + /// Checks if volumes were removed. #[must_use] pub fn volumes_removed(&self) -> bool { self.volumes_removed diff --git a/src/command/compose/run.rs b/src/command/compose/run.rs index 1fc51625..178658a9 100644 --- a/src/command/compose/run.rs +++ b/src/command/compose/run.rs @@ -1,71 +1,74 @@ //! Docker Compose run command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::collections::HashMap; -/// Docker Compose run command builder +/// Docker Compose run command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are appropriate for run command +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are appropriate for run command pub struct ComposeRunCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Service to run + /// Service to run. pub service: String, - /// Command and arguments to run + /// Command and arguments to run. pub command: Vec, - /// Run container in background + /// Runs container in background. pub detach: bool, - /// Automatically remove the container when it exits + /// Automatically removes the container when it exits. pub rm: bool, - /// Don't start linked services + /// Doesn't start linked services. pub no_deps: bool, - /// Disable pseudo-TTY allocation + /// Disables pseudo-TTY allocation. pub no_tty: bool, - /// Keep STDIN open even if not attached + /// Keeps STDIN open even if not attached. pub interactive: bool, - /// Override the entrypoint + /// Overrides the entrypoint. pub entrypoint: Option, - /// Set environment variables + /// Sets environment variables. pub env: HashMap, - /// Add or override labels + /// Adds or overrides labels. pub labels: HashMap, - /// Container name + /// Container name. pub name: Option, - /// Publish container ports to host + /// Publishes container ports to host. pub publish: Vec, - /// Run as specified user + /// Runs as specified user. pub user: Option, - /// Working directory inside the container + /// Working directory inside the container. pub workdir: Option, - /// Bind mount volumes + /// Binds mount volumes. pub volumes: Vec, - /// Remove associated volumes when container is removed + /// Removes associated volumes when container is removed. pub volume_rm: bool, } -/// Result from compose run command +/// Result from compose run command. #[derive(Debug, Clone)] pub struct ComposeRunResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Exit code from the container + /// Exit code from the container. pub exit_code: i32, - /// Service that was run + /// Service that was run. pub service: String, - /// Whether the container was run in detached mode + /// Whether the container was run in detached mode. pub detached: bool, } impl ComposeRunCommand { - /// Create a new compose run command + /// Creates a new compose run command. #[must_use] pub fn new(service: impl Into) -> Self { Self { @@ -90,7 +93,7 @@ impl ComposeRunCommand { } } - /// Set the command to run + /// Sets the command to run. #[must_use] pub fn cmd(mut self, command: I) -> Self where @@ -101,14 +104,14 @@ impl ComposeRunCommand { self } - /// Add command arguments + /// Adds a command argument. #[must_use] pub fn arg(mut self, arg: impl Into) -> Self { self.command.push(arg.into()); self } - /// Add multiple arguments + /// Adds multiple arguments. #[must_use] pub fn args(mut self, args: I) -> Self where @@ -119,112 +122,112 @@ impl ComposeRunCommand { self } - /// Run container in background + /// Runs container in background. #[must_use] pub fn detach(mut self) -> Self { self.detach = true; self } - /// Automatically remove the container when it exits + /// Automatically removes the container when it exits. #[must_use] pub fn rm(mut self) -> Self { self.rm = true; self } - /// Don't start linked services + /// Doesn't start linked services. #[must_use] pub fn no_deps(mut self) -> Self { self.no_deps = true; self } - /// Disable pseudo-TTY allocation + /// Disables pseudo-TTY allocation. #[must_use] pub fn no_tty(mut self) -> Self { self.no_tty = true; self } - /// Keep STDIN open even if not attached + /// Keeps STDIN open even if not attached. #[must_use] pub fn interactive(mut self) -> Self { self.interactive = true; self } - /// Override the entrypoint + /// Overrides the entrypoint. #[must_use] pub fn entrypoint(mut self, entrypoint: impl Into) -> Self { self.entrypoint = Some(entrypoint.into()); self } - /// Set an environment variable + /// Sets an environment variable. #[must_use] pub fn env(mut self, key: impl Into, value: impl Into) -> Self { self.env.insert(key.into(), value.into()); self } - /// Set multiple environment variables + /// Sets multiple environment variables. #[must_use] pub fn envs(mut self, env_vars: HashMap) -> Self { self.env.extend(env_vars); self } - /// Add or override a label + /// Adds or overrides a label. #[must_use] pub fn label(mut self, key: impl Into, value: impl Into) -> Self { self.labels.insert(key.into(), value.into()); self } - /// Set multiple labels + /// Sets multiple labels. #[must_use] pub fn labels(mut self, labels: HashMap) -> Self { self.labels.extend(labels); self } - /// Set container name + /// Sets container name. #[must_use] pub fn name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self } - /// Publish a port to the host + /// Publishes a port to the host. #[must_use] pub fn publish(mut self, publish: impl Into) -> Self { self.publish.push(publish.into()); self } - /// Run as specified user + /// Runs as specified user. #[must_use] pub fn user(mut self, user: impl Into) -> Self { self.user = Some(user.into()); self } - /// Set working directory inside the container + /// Sets working directory inside the container. #[must_use] pub fn workdir(mut self, workdir: impl Into) -> Self { self.workdir = Some(workdir.into()); self } - /// Bind mount a volume + /// Binds mount a volume. #[must_use] pub fn volume(mut self, volume: impl Into) -> Self { self.volumes.push(volume.into()); self } - /// Remove associated volumes when container is removed + /// Removes associated volumes when container is removed. #[must_use] pub fn volume_rm(mut self) -> Self { self.volume_rm = true; @@ -236,6 +239,10 @@ impl ComposeRunCommand { impl DockerCommand for ComposeRunCommand { type Output = ComposeRunResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -245,7 +252,6 @@ impl DockerCommand for ComposeRunCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -265,6 +271,10 @@ impl DockerCommand for ComposeRunCommand { } impl ComposeCommand for ComposeRunCommand { + fn subcommand_name() -> &'static str { + "run" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -273,10 +283,6 @@ impl ComposeCommand for ComposeRunCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "run" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -300,49 +306,49 @@ impl ComposeCommand for ComposeRunCommand { args.push("--interactive".to_string()); } - // Add entrypoint + // add entrypoint if let Some(ref entrypoint) = self.entrypoint { args.push("--entrypoint".to_string()); args.push(entrypoint.clone()); } - // Add environment variables + // add environment variables for (key, value) in &self.env { args.push("--env".to_string()); args.push(format!("{key}={value}")); } - // Add labels + // add labels for (key, value) in &self.labels { args.push("--label".to_string()); args.push(format!("{key}={value}")); } - // Add container name + // add container name if let Some(ref name) = self.name { args.push("--name".to_string()); args.push(name.clone()); } - // Add published ports + // add published ports for publish in &self.publish { args.push("--publish".to_string()); args.push(publish.clone()); } - // Add user + // add user if let Some(ref user) = self.user { args.push("--user".to_string()); args.push(user.clone()); } - // Add working directory + // add working directory if let Some(ref workdir) = self.workdir { args.push("--workdir".to_string()); args.push(workdir.clone()); } - // Add volumes + // add volumes for volume in &self.volumes { args.push("--volume".to_string()); args.push(volume.clone()); @@ -353,10 +359,10 @@ impl ComposeCommand for ComposeRunCommand { args.push("rm".to_string()); } - // Add service name + // add service name args.push(self.service.clone()); - // Add command and arguments + // add command and arguments args.extend(self.command.clone()); args @@ -364,25 +370,25 @@ impl ComposeCommand for ComposeRunCommand { } impl ComposeRunResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the exit code from the container + /// Gets the exit code from the container. #[must_use] pub fn exit_code(&self) -> i32 { self.exit_code } - /// Get the service that was run + /// Gets the service that was run. #[must_use] pub fn service(&self) -> &str { &self.service } - /// Check if the container was run in detached mode + /// Checks if the container was run in detached mode. #[must_use] pub fn is_detached(&self) -> bool { self.detached @@ -464,11 +470,11 @@ mod tests { let args = cmd.build_subcommand_args(); - // Check flags + // check flags assert!(args.contains(&"--detach".to_string())); assert!(args.contains(&"--rm".to_string())); - // Check named parameters + // check named parameters assert!(args.contains(&"--name".to_string())); assert!(args.contains(&"test-db".to_string())); assert!(args.contains(&"--user".to_string())); @@ -482,11 +488,11 @@ mod tests { assert!(args.contains(&"--entrypoint".to_string())); assert!(args.contains(&"docker-entrypoint.sh".to_string())); - // Check service and command + // check service and command assert!(args.contains(&"database".to_string())); assert!(args.contains(&"postgres".to_string())); - // Check env and labels + // check env and labels assert!(args.contains(&"POSTGRES_DB=testdb".to_string())); assert!(args.contains(&"env=test".to_string())); } diff --git a/src/command/compose/scale.rs b/src/command/compose/scale.rs index 5f8ca40d..9d36f96d 100644 --- a/src/command/compose/scale.rs +++ b/src/command/compose/scale.rs @@ -1,38 +1,41 @@ //! Docker Compose scale command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::collections::HashMap; -/// Docker Compose scale command builder +/// Docker Compose scale command builder. #[derive(Debug, Clone)] pub struct ComposeScaleCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Service scaling configurations (`service_name` -> replicas) + /// Service scaling configurations (`service_name` -> replicas). pub services: HashMap, - /// Don't start linked services + /// Doesn't start linked services. pub no_deps: bool, } -/// Result from compose scale command +/// Result from compose scale command. #[derive(Debug, Clone)] pub struct ComposeScaleResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were scaled + /// Services that were scaled. pub scaled_services: HashMap, } impl ComposeScaleCommand { - /// Create a new compose scale command + /// Creates a new compose scale command. #[must_use] pub fn new() -> Self { Self { @@ -43,21 +46,21 @@ impl ComposeScaleCommand { } } - /// Add a service to scale + /// Adds a service to scale. #[must_use] pub fn service(mut self, service: impl Into, replicas: u32) -> Self { self.services.insert(service.into(), replicas); self } - /// Add multiple services to scale + /// Adds multiple services to scale. #[must_use] pub fn services(mut self, services: HashMap) -> Self { self.services.extend(services); self } - /// Don't start linked services + /// Doesn't start linked services. #[must_use] pub fn no_deps(mut self) -> Self { self.no_deps = true; @@ -75,11 +78,15 @@ impl Default for ComposeScaleCommand { impl DockerCommand for ComposeScaleCommand { type Output = ComposeScaleResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -101,16 +108,16 @@ impl DockerCommand for ComposeScaleCommand { } impl ComposeCommand for ComposeScaleCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "scale" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "scale" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -129,13 +136,13 @@ impl ComposeCommand for ComposeScaleCommand { } impl ComposeScaleResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were scaled + /// Gets the services that were scaled. #[must_use] pub fn scaled_services(&self) -> &HashMap { &self.scaled_services diff --git a/src/command/compose/start.rs b/src/command/compose/start.rs index e96e0142..045bcb8d 100644 --- a/src/command/compose/start.rs +++ b/src/command/compose/start.rs @@ -1,35 +1,38 @@ //! Docker Compose start command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose start command builder +/// Docker Compose start command builder. #[derive(Debug, Clone)] pub struct ComposeStartCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to start (empty for all) + /// Services to start (empty for all). pub services: Vec, } -/// Result from compose start command +/// Result from compose start command. #[derive(Debug, Clone)] pub struct ComposeStartResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were started + /// Services that were started. pub services: Vec, } impl ComposeStartCommand { - /// Create a new compose start command + /// Creates a new compose start command. #[must_use] pub fn new() -> Self { Self { @@ -39,14 +42,14 @@ impl ComposeStartCommand { } } - /// Add a service to start + /// Adds a service to start. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to start + /// Adds multiple services to start. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -68,6 +71,10 @@ impl Default for ComposeStartCommand { impl DockerCommand for ComposeStartCommand { type Output = ComposeStartResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -77,7 +84,6 @@ impl DockerCommand for ComposeStartCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -95,6 +101,10 @@ impl DockerCommand for ComposeStartCommand { } impl ComposeCommand for ComposeStartCommand { + fn subcommand_name() -> &'static str { + "start" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -103,24 +113,19 @@ impl ComposeCommand for ComposeStartCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "start" - } - fn build_subcommand_args(&self) -> Vec { - // Start command just takes service names self.services.clone() } } impl ComposeStartResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were started + /// Gets the services that were started. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/stop.rs b/src/command/compose/stop.rs index d1c52066..6f597857 100644 --- a/src/command/compose/stop.rs +++ b/src/command/compose/stop.rs @@ -1,38 +1,41 @@ //! Docker Compose stop command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::time::Duration; -/// Docker Compose stop command builder +/// Docker Compose stop command builder. #[derive(Debug, Clone)] pub struct ComposeStopCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to stop (empty for all) + /// Services to stop (empty for all). pub services: Vec, - /// Timeout for stopping containers + /// Timeout for stopping containers. pub timeout: Option, } -/// Result from compose stop command +/// Result from compose stop command. #[derive(Debug, Clone)] pub struct ComposeStopResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were stopped + /// Services that were stopped. pub services: Vec, } impl ComposeStopCommand { - /// Create a new compose stop command + /// Creates a new compose stop command. #[must_use] pub fn new() -> Self { Self { @@ -43,14 +46,14 @@ impl ComposeStopCommand { } } - /// Add a service to stop + /// Adds a service to stop. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to stop + /// Adds multiple services to stop. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -61,7 +64,7 @@ impl ComposeStopCommand { self } - /// Set the timeout for stopping containers + /// Sets the timeout for stopping containers. #[must_use] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); @@ -79,6 +82,10 @@ impl Default for ComposeStopCommand { impl DockerCommand for ComposeStopCommand { type Output = ComposeStopResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -88,7 +95,6 @@ impl DockerCommand for ComposeStopCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -106,6 +112,10 @@ impl DockerCommand for ComposeStopCommand { } impl ComposeCommand for ComposeStopCommand { + fn subcommand_name() -> &'static str { + "stop" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -114,10 +124,6 @@ impl ComposeCommand for ComposeStopCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "stop" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -134,13 +140,13 @@ impl ComposeCommand for ComposeStopCommand { } impl ComposeStopResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were stopped + /// Gets the services that were stopped. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/top.rs b/src/command/compose/top.rs index fdbf7de0..a64a8777 100644 --- a/src/command/compose/top.rs +++ b/src/command/compose/top.rs @@ -1,35 +1,38 @@ //! Docker Compose top command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose top command builder +/// Docker Compose top command builder. #[derive(Debug, Clone)] pub struct ComposeTopCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to show processes for (empty for all) + /// Services to show processes for (empty for all). pub services: Vec, } -/// Result from compose top command +/// Result from compose top command. #[derive(Debug, Clone)] pub struct ComposeTopResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were queried + /// Services that were queried. pub services: Vec, } impl ComposeTopCommand { - /// Create a new compose top command + /// Creates a new compose top command. #[must_use] pub fn new() -> Self { Self { @@ -39,14 +42,14 @@ impl ComposeTopCommand { } } - /// Add a service to show processes for + /// Adds a service to show processes for. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to show processes for + /// Adds multiple services to show processes for. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -68,11 +71,15 @@ impl Default for ComposeTopCommand { impl DockerCommand for ComposeTopCommand { type Output = ComposeTopResult; - fn get_executor(&self) -> &CommandExecutor { + fn command_name() -> &'static str { + ::command_name() + } + + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -94,16 +101,16 @@ impl DockerCommand for ComposeTopCommand { } impl ComposeCommand for ComposeTopCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "top" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "top" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -112,13 +119,13 @@ impl ComposeCommand for ComposeTopCommand { } impl ComposeTopResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were queried + /// Gets the services that were queried. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/unpause.rs b/src/command/compose/unpause.rs index 180206a2..1260f13c 100644 --- a/src/command/compose/unpause.rs +++ b/src/command/compose/unpause.rs @@ -1,35 +1,38 @@ //! Docker Compose unpause command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose unpause command builder +/// Docker Compose unpause command builder. #[derive(Debug, Clone)] pub struct ComposeUnpauseCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to unpause (empty for all) + /// Services to unpause (empty for all). pub services: Vec, } -/// Result from compose unpause command +/// Result from compose unpause command. #[derive(Debug, Clone)] pub struct ComposeUnpauseResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were unpaused + /// Services that were unpaused. pub services: Vec, } impl ComposeUnpauseCommand { - /// Create a new compose unpause command + /// Creates a new compose unpause command. #[must_use] pub fn new() -> Self { Self { @@ -39,14 +42,14 @@ impl ComposeUnpauseCommand { } } - /// Add a service to unpause + /// Adds a service to unpause. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to unpause + /// Adds multiple services to unpause. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -68,6 +71,10 @@ impl Default for ComposeUnpauseCommand { impl DockerCommand for ComposeUnpauseCommand { type Output = ComposeUnpauseResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -77,7 +84,6 @@ impl DockerCommand for ComposeUnpauseCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -95,6 +101,10 @@ impl DockerCommand for ComposeUnpauseCommand { } impl ComposeCommand for ComposeUnpauseCommand { + fn subcommand_name() -> &'static str { + "unpause" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -103,24 +113,19 @@ impl ComposeCommand for ComposeUnpauseCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "unpause" - } - fn build_subcommand_args(&self) -> Vec { - // Unpause command just takes service names self.services.clone() } } impl ComposeUnpauseResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were unpaused + /// Gets the services that were unpaused. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/command/compose/up.rs b/src/command/compose/up.rs index 955cf807..041c528b 100644 --- a/src/command/compose/up.rs +++ b/src/command/compose/up.rs @@ -1,66 +1,70 @@ //! Docker Compose up command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::time::Duration; -/// Docker Compose up command builder +/// Docker Compose up command builder. #[derive(Debug, Clone)] -#[allow(clippy::struct_excessive_bools)] // Multiple boolean flags are needed for compose up options +#[allow(clippy::struct_excessive_bools)] // multiple boolean flags are needed for compose up options pub struct ComposeUpCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to start (empty for all) + /// Services to start (empty for all). pub services: Vec, - /// Run in detached mode + /// Runs in detached mode. pub detach: bool, - /// Don't start linked services + /// Doesn't start linked services. pub no_deps: bool, - /// Force recreate containers + /// Force recreates containers. pub force_recreate: bool, - /// Recreate containers even if configuration unchanged + /// Recreates containers even if configuration unchanged. pub always_recreate_deps: bool, - /// Don't recreate containers + /// Doesn't recreate containers. pub no_recreate: bool, - /// Don't build images + /// Doesn't build images. pub no_build: bool, - /// Don't start services + /// Doesn't start services. pub no_start: bool, - /// Build images before starting + /// Builds images before starting. pub build: bool, - /// Remove orphan containers + /// Removes orphan containers. pub remove_orphans: bool, - /// Scale SERVICE to NUM instances + /// Scales SERVICE to NUM instances. pub scale: Vec<(String, u32)>, - /// Timeout for container shutdown + /// Timeout for container shutdown. pub timeout: Option, - /// Exit code from first container that stops + /// Exit code from first container that stops. pub exit_code_from: Option, - /// Abort if containers are stopped + /// Aborts if containers are stopped. pub abort_on_container_exit: bool, - /// Attach to dependent containers + /// Attaches to dependent containers. pub attach_dependencies: bool, - /// Recreate anonymous volumes + /// Recreates anonymous volumes. pub renew_anon_volumes: bool, - /// Wait for services to be healthy + /// Waits for services to be healthy. pub wait: bool, - /// Maximum wait timeout + /// Maximum wait timeout. pub wait_timeout: Option, - /// Pull image policy + /// Image pulling policy. pub pull: Option, } -/// Image pull policy -#[derive(Debug, Clone, Copy)] +/// Image pulling policy +#[derive(Debug, Default, Clone, Copy)] pub enum PullPolicy { - /// Always pull images + /// Always pulls images. Always, - /// Never pull images + /// Never pulls images. Never, - /// Pull missing images (default) + /// Pulls missing images (default). + #[default] Missing, } @@ -74,23 +78,23 @@ impl std::fmt::Display for PullPolicy { } } -/// Result from compose up command +/// Result from compose up command. #[derive(Debug, Clone)] pub struct ComposeUpResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were started + /// Services that were started. pub services: Vec, - /// Whether running in detached mode + /// Whether running in detached mode. pub detached: bool, } impl ComposeUpCommand { - /// Create a new compose up command + /// Creates a new compose up command. #[must_use] pub fn new() -> Self { Self { @@ -118,14 +122,14 @@ impl ComposeUpCommand { } } - /// Add a service to start + /// Adds a service to start. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services + /// Adds multiple services. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -136,126 +140,126 @@ impl ComposeUpCommand { self } - /// Run in detached mode + /// Runs in detached mode. #[must_use] pub fn detach(mut self) -> Self { self.detach = true; self } - /// Don't start linked services + /// Doesn't start linked services. #[must_use] pub fn no_deps(mut self) -> Self { self.no_deps = true; self } - /// Force recreate containers + /// Force recreates containers. #[must_use] pub fn force_recreate(mut self) -> Self { self.force_recreate = true; self } - /// Always recreate dependent containers + /// Always recreates dependent containers. #[must_use] pub fn always_recreate_deps(mut self) -> Self { self.always_recreate_deps = true; self } - /// Don't recreate containers + /// Doesn't recreate containers. #[must_use] pub fn no_recreate(mut self) -> Self { self.no_recreate = true; self } - /// Don't build images + /// Doesn't build images. #[must_use] pub fn no_build(mut self) -> Self { self.no_build = true; self } - /// Don't start services after creating + /// Doesn't start services after creating. #[must_use] pub fn no_start(mut self) -> Self { self.no_start = true; self } - /// Build images before starting + /// Builds images before starting. #[must_use] pub fn build(mut self) -> Self { self.build = true; self } - /// Remove orphan containers + /// Removes orphan containers. #[must_use] pub fn remove_orphans(mut self) -> Self { self.remove_orphans = true; self } - /// Scale a service to a specific number of instances + /// Scales a service to a specific number of instances. #[must_use] pub fn scale(mut self, service: impl Into, instances: u32) -> Self { self.scale.push((service.into(), instances)); self } - /// Set timeout for container shutdown + /// Sets timeout for container shutdown. #[must_use] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); self } - /// Use exit code from specific container + /// Uses exit code from specific container. #[must_use] pub fn exit_code_from(mut self, service: impl Into) -> Self { self.exit_code_from = Some(service.into()); self } - /// Abort when containers stop + /// Aborts when containers stop. #[must_use] pub fn abort_on_container_exit(mut self) -> Self { self.abort_on_container_exit = true; self } - /// Attach to dependent containers + /// Attaches to dependent containers. #[must_use] pub fn attach_dependencies(mut self) -> Self { self.attach_dependencies = true; self } - /// Recreate anonymous volumes + /// Recreates anonymous volumes. #[must_use] pub fn renew_anon_volumes(mut self) -> Self { self.renew_anon_volumes = true; self } - /// Wait for services to be running/healthy + /// Waits for services to be running/healthy. #[must_use] pub fn wait(mut self) -> Self { self.wait = true; self } - /// Set maximum wait timeout + /// Sets maximum wait timeout. #[must_use] pub fn wait_timeout(mut self, timeout: Duration) -> Self { self.wait_timeout = Some(timeout); self } - /// Set pull policy + /// Sets image pulling policy. #[must_use] pub fn pull(mut self, policy: PullPolicy) -> Self { self.pull = Some(policy); @@ -273,6 +277,10 @@ impl Default for ComposeUpCommand { impl DockerCommand for ComposeUpCommand { type Output = ComposeUpResult; + fn command_name() -> &'static str { + ::command_name() + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -282,7 +290,6 @@ impl DockerCommand for ComposeUpCommand { } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -301,6 +308,10 @@ impl DockerCommand for ComposeUpCommand { } impl ComposeCommand for ComposeUpCommand { + fn subcommand_name() -> &'static str { + "up" + } + fn config(&self) -> &ComposeConfig { &self.config } @@ -309,10 +320,6 @@ impl ComposeCommand for ComposeUpCommand { &mut self.config } - fn subcommand(&self) -> &'static str { - "up" - } - fn build_subcommand_args(&self) -> Vec { let mut args = Vec::new(); @@ -393,7 +400,7 @@ impl ComposeCommand for ComposeUpCommand { args.push(pull.to_string()); } - // Add service names at the end + // add service names at the end args.extend(self.services.clone()); args @@ -401,19 +408,19 @@ impl ComposeCommand for ComposeUpCommand { } impl ComposeUpResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were started + /// Gets the services that were started. #[must_use] pub fn services(&self) -> &[String] { &self.services } - /// Check if running in detached mode + /// Checks if running in detached mode. #[must_use] pub fn is_detached(&self) -> bool { self.detached diff --git a/src/command/compose/version.rs b/src/command/compose/version.rs index 3f4ff7e6..a7b620b8 100644 --- a/src/command/compose/version.rs +++ b/src/command/compose/version.rs @@ -1,29 +1,33 @@ //! Docker Compose version command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use serde::Deserialize; -/// Docker Compose version command builder +/// Docker Compose version command builder. #[derive(Debug, Clone)] pub struct ComposeVersionCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Format output (pretty, json) + /// Format output (pretty, json). pub format: Option, - /// Short output + /// Short output. pub short: bool, } -/// Version output format -#[derive(Debug, Clone, Copy)] +/// Version output format. +#[derive(Debug, Default, Clone, Copy)] pub enum VersionFormat { - /// Pretty format (default) + /// Pretty format (default). + #[default] Pretty, - /// JSON format + /// JSON format. Json, } @@ -36,29 +40,29 @@ impl std::fmt::Display for VersionFormat { } } -/// Version information from JSON output +/// Version information from JSON output. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct VersionInfo { - /// Compose version + /// Compose version. pub version: String, } -/// Result from compose version command +/// Result from compose version command. #[derive(Debug, Clone)] pub struct ComposeVersionResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Parsed version information (if JSON format) + /// Parsed version information (if JSON format). pub version_info: Option, } impl ComposeVersionCommand { - /// Create a new compose version command + /// Creates a new compose version command. #[must_use] pub fn new() -> Self { Self { @@ -69,28 +73,28 @@ impl ComposeVersionCommand { } } - /// Set output format + /// Sets output format. #[must_use] pub fn format(mut self, format: VersionFormat) -> Self { self.format = Some(format); self } - /// Set output format to JSON + /// Sets output format to JSON. #[must_use] pub fn format_json(mut self) -> Self { self.format = Some(VersionFormat::Json); self } - /// Set output format to pretty + /// Sets output format to pretty. #[must_use] pub fn format_pretty(mut self) -> Self { self.format = Some(VersionFormat::Pretty); self } - /// Enable short output + /// Enables short output. #[must_use] pub fn short(mut self) -> Self { self.short = true; @@ -106,18 +110,20 @@ impl Default for ComposeVersionCommand { #[async_trait] impl DockerCommand for ComposeVersionCommand { + fn command_name() -> &'static str { + ::command_name() + } type Output = ComposeVersionResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } fn build_command_args(&self) -> Vec { - // Use the ComposeCommand implementation explicitly ::build_command_args(self) } @@ -125,7 +131,7 @@ impl DockerCommand for ComposeVersionCommand { let args = ::build_command_args(self); let output = self.execute_command(args).await?; - // Parse JSON output if format is JSON + // parses JSON output if format is JSON let version_info = if matches!(self.format, Some(VersionFormat::Json)) { serde_json::from_str(&output.stdout).ok() } else { @@ -142,16 +148,16 @@ impl DockerCommand for ComposeVersionCommand { } impl ComposeCommand for ComposeVersionCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "version" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "version" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -171,25 +177,25 @@ impl ComposeCommand for ComposeVersionCommand { } impl ComposeVersionResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get parsed version information (if JSON format was used) + /// Gets parsed version information (if JSON format was used). #[must_use] pub fn version_info(&self) -> Option<&VersionInfo> { self.version_info.as_ref() } - /// Get the version string (from parsed info or raw output) + /// Gets the version string (from parsed info or raw output). #[must_use] pub fn version_string(&self) -> Option { if let Some(info) = &self.version_info { Some(info.version.clone()) } else { - // Try to extract version from raw output + // tries to extract version from raw output self.stdout .lines() .find(|line| line.contains("version")) diff --git a/src/command/compose/wait.rs b/src/command/compose/wait.rs index a26e85a2..e452af78 100644 --- a/src/command/compose/wait.rs +++ b/src/command/compose/wait.rs @@ -1,42 +1,45 @@ //! Docker Compose wait command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; use std::time::Duration; -/// Docker Compose wait command builder +/// Docker Compose wait command builder. #[derive(Debug, Clone)] pub struct ComposeWaitCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to wait for (empty for all) + /// Services to wait for (empty for all). pub services: Vec, - /// Timeout for waiting + /// Timeout for waiting. pub timeout: Option, - /// Wait for services to be healthy (with health checks) + /// Waits for services to be healthy (with health checks). pub wait_for_healthy: bool, } -/// Result from compose wait command +/// Result from compose wait command. #[derive(Debug, Clone)] pub struct ComposeWaitResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were waited for + /// Services that were waited for. pub services: Vec, - /// Whether all services became ready/healthy + /// Whether all services became ready/healthy. pub all_ready: bool, } impl ComposeWaitCommand { - /// Create a new compose wait command + /// Creates a new compose wait command. #[must_use] pub fn new() -> Self { Self { @@ -48,14 +51,14 @@ impl ComposeWaitCommand { } } - /// Add a service to wait for + /// Adds a service to wait for. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to wait for + /// Adds multiple services to wait for. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -66,14 +69,14 @@ impl ComposeWaitCommand { self } - /// Set timeout for waiting + /// Sets timeout for waiting. #[must_use] pub fn timeout(mut self, timeout: Duration) -> Self { self.timeout = Some(timeout); self } - /// Wait for services to be healthy (requires health checks) + /// Waits for services to be healthy (requires health checks). #[must_use] pub fn wait_for_healthy(mut self) -> Self { self.wait_for_healthy = true; @@ -89,13 +92,16 @@ impl Default for ComposeWaitCommand { #[async_trait] impl DockerCommand for ComposeWaitCommand { + fn command_name() -> &'static str { + ::command_name() + } type Output = ComposeWaitResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -118,16 +124,16 @@ impl DockerCommand for ComposeWaitCommand { } impl ComposeCommand for ComposeWaitCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "wait" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "wait" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -148,19 +154,19 @@ impl ComposeCommand for ComposeWaitCommand { } impl ComposeWaitResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were waited for + /// Gets the services that were waited for. #[must_use] pub fn services(&self) -> &[String] { &self.services } - /// Check if all services became ready/healthy + /// Checks if all services became ready/healthy. #[must_use] pub fn all_ready(&self) -> bool { self.all_ready diff --git a/src/command/compose/watch.rs b/src/command/compose/watch.rs index 80659ed8..cc779d4f 100644 --- a/src/command/compose/watch.rs +++ b/src/command/compose/watch.rs @@ -1,39 +1,42 @@ //! Docker Compose watch command implementation using unified trait pattern. -use super::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand}; -use crate::error::Result; +use crate::{ + compose::{ComposeCommand, ComposeConfig}, + error::Result, + CommandExecutor, DockerCommand, +}; use async_trait::async_trait; -/// Docker Compose watch command builder +/// Docker Compose watch command builder. #[derive(Debug, Clone)] pub struct ComposeWatchCommand { - /// Base command executor + /// Base command executor. pub executor: CommandExecutor, - /// Base compose configuration + /// Base compose configuration. pub config: ComposeConfig, - /// Services to watch for changes (empty for all) + /// Services to watch for changes (empty for all). pub services: Vec, - /// Don't build images before starting + /// Doesn't build images before starting. pub no_up: bool, - /// Quiet mode + /// Quiet mode. pub quiet: bool, } -/// Result from compose watch command +/// Result from compose watch command. #[derive(Debug, Clone)] pub struct ComposeWatchResult { - /// Raw stdout output + /// Raw stdout output. pub stdout: String, - /// Raw stderr output + /// Raw stderr output. pub stderr: String, - /// Success status + /// Success status. pub success: bool, - /// Services that were watched + /// Services that were watched. pub services: Vec, } impl ComposeWatchCommand { - /// Create a new compose watch command + /// Creates a new compose watch command. #[must_use] pub fn new() -> Self { Self { @@ -45,14 +48,14 @@ impl ComposeWatchCommand { } } - /// Add a service to watch + /// Adds a service to watch. #[must_use] pub fn service(mut self, service: impl Into) -> Self { self.services.push(service.into()); self } - /// Add multiple services to watch + /// Adds multiple services to watch. #[must_use] pub fn services(mut self, services: I) -> Self where @@ -63,14 +66,14 @@ impl ComposeWatchCommand { self } - /// Don't build images before starting + /// Doesn't build images before starting. #[must_use] pub fn no_up(mut self) -> Self { self.no_up = true; self } - /// Enable quiet mode + /// Enables quiet mode. #[must_use] pub fn quiet(mut self) -> Self { self.quiet = true; @@ -86,13 +89,16 @@ impl Default for ComposeWatchCommand { #[async_trait] impl DockerCommand for ComposeWatchCommand { + fn command_name() -> &'static str { + ::command_name() + } type Output = ComposeWatchResult; - fn get_executor(&self) -> &CommandExecutor { + fn executor(&self) -> &CommandExecutor { &self.executor } - fn get_executor_mut(&mut self) -> &mut CommandExecutor { + fn executor_mut(&mut self) -> &mut CommandExecutor { &mut self.executor } @@ -114,16 +120,16 @@ impl DockerCommand for ComposeWatchCommand { } impl ComposeCommand for ComposeWatchCommand { - fn get_config(&self) -> &ComposeConfig { - &self.config + fn subcommand_name() -> &'static str { + "watch" } - fn get_config_mut(&mut self) -> &mut ComposeConfig { - &mut self.config + fn config(&self) -> &ComposeConfig { + &self.config } - fn subcommand(&self) -> &'static str { - "watch" + fn config_mut(&mut self) -> &mut ComposeConfig { + &mut self.config } fn build_subcommand_args(&self) -> Vec { @@ -143,13 +149,13 @@ impl ComposeCommand for ComposeWatchCommand { } impl ComposeWatchResult { - /// Check if the command was successful + /// Checks if the command was successful. #[must_use] pub fn success(&self) -> bool { self.success } - /// Get the services that were watched + /// Gets the services that were watched. #[must_use] pub fn services(&self) -> &[String] { &self.services diff --git a/src/compose.rs b/src/compose.rs index 54e10aa9..3ac77e62 100644 --- a/src/compose.rs +++ b/src/compose.rs @@ -175,33 +175,37 @@ impl ComposeConfig { /// Extended trait for Docker Compose commands. pub trait ComposeCommand: DockerCommand { + /// Gets the base command name. This should always be `compose`. + fn command_name() -> &'static str { + "compose" + } + + /// Gets the full subcommand name (e.g., `up`, `down`), which will be appended to the base command. + fn subcommand_name() -> &'static str; + /// Gets the compose configuration. fn config(&self) -> &ComposeConfig; /// Gets the mutable compose configuration for builder pattern. fn config_mut(&mut self) -> &mut ComposeConfig; - /// Gets the compose subcommand name (e.g., `up`, `down`, `ps`). - fn subcommand(&self) -> &'static str; - /// Builds command-specific arguments (without global compose args). fn build_subcommand_args(&self) -> Vec; - /// Builds complete command arguments including "compose" and global args. - /// This provides the implementation for `DockerCommandV2::build_command_args`. + /// Builds complete command arguments including subcommand name and global args. fn build_command_args(&self) -> Vec { - let mut args = vec!["compose".to_string()]; + let mut args = vec![Self::subcommand_name().to_string()]; - // Adds global compose arguments. + // add global compose arguments args.extend(self.config().build_global_args()); - // Adds the subcommand. + // add the subcommand args.push(self.subcommand().to_string()); - // Adds command-specific arguments. + // add command-specific arguments args.extend(self.build_subcommand_args()); - // Adds raw arguments from executor. + // add raw arguments from executor args.extend(self.executor().raw_args.clone()); args diff --git a/tests/images_integration.rs b/tests/images_integration.rs index b6f645c3..9a358853 100644 --- a/tests/images_integration.rs +++ b/tests/images_integration.rs @@ -429,7 +429,7 @@ async fn test_images_command_validation() { let test_cases = vec![ (ImagesCommand::new(), 0), // Basic command (ImagesCommand::new().all(), 1), // With --all - (ImagesCommand::new().quiet().no_trunc(), 2), // Multiple flags + (ImagesCommand::new().quiet().no_trunc(), 2), // multiple flags (ImagesCommand::new().repository("nginx").digests(), 2), // With repository ]; From 8f914aafa1af84f0cd991c39c5778589a434326c Mon Sep 17 00:00:00 2001 From: KrLite Date: Fri, 21 Nov 2025 16:24:49 +0800 Subject: [PATCH 9/9] feat: more commands --- src/command/attach.rs | 51 +++++---- src/command/bake.rs | 198 ++++++++++++++++++--------------- src/command/compose/attach.rs | 8 +- src/command/compose/build.rs | 20 ++-- src/command/compose/config.rs | 4 +- src/command/compose/convert.rs | 4 +- src/command/compose/cp.rs | 4 +- src/command/compose/create.rs | 4 +- src/command/compose/down.rs | 4 +- src/command/compose/events.rs | 4 +- src/command/compose/exec.rs | 4 +- src/command/compose/images.rs | 4 +- src/command/compose/kill.rs | 4 +- src/command/compose/logs.rs | 4 +- src/command/compose/ls.rs | 4 +- src/command/compose/pause.rs | 4 +- src/command/compose/port.rs | 4 +- src/command/compose/ps.rs | 4 +- src/command/compose/push.rs | 4 +- src/command/compose/restart.rs | 4 +- src/command/compose/rm.rs | 4 +- src/command/compose/run.rs | 12 +- src/command/compose/scale.rs | 4 +- src/command/compose/start.rs | 4 +- src/command/compose/stop.rs | 4 +- src/command/compose/top.rs | 4 +- src/command/compose/unpause.rs | 4 +- src/command/compose/up.rs | 4 +- src/command/compose/version.rs | 4 +- src/command/compose/wait.rs | 4 +- src/command/compose/watch.rs | 4 +- src/compose.rs | 4 +- 32 files changed, 208 insertions(+), 189 deletions(-) diff --git a/src/command/attach.rs b/src/command/attach.rs index bf0adf02..789c4d32 100644 --- a/src/command/attach.rs +++ b/src/command/attach.rs @@ -6,7 +6,7 @@ use super::{CommandExecutor, CommandOutput, DockerCommand}; use crate::error::Result; use async_trait::async_trait; -/// Docker attach command builder +/// Docker attach command builder. /// /// Attach local standard input, output, and error streams to a running container. /// @@ -16,12 +16,12 @@ use async_trait::async_trait; /// use docker_wrapper::AttachCommand; /// /// # async fn example() -> Result<(), Box> { -/// // Attach to a running container +/// // attach to a running container /// AttachCommand::new("my-container") /// .run() /// .await?; /// -/// // Attach without stdin +/// // attach without stdin /// AttachCommand::new("my-container") /// .no_stdin() /// .run() @@ -31,20 +31,20 @@ use async_trait::async_trait; /// ``` #[derive(Debug, Clone)] pub struct AttachCommand { - /// Container name or ID + /// Container name or ID. container: String, - /// Override the key sequence for detaching + /// Overrides the key sequence for detaching. detach_keys: Option, - /// Do not attach STDIN + /// Doesn't attach STDIN. no_stdin: bool, - /// Proxy all received signals to the process + /// Proxies all received signals to the process. sig_proxy: bool, - /// Command executor + /// Command executor. pub executor: CommandExecutor, } impl AttachCommand { - /// Create a new attach command + /// Creates a new [`AttachCommand`]. /// /// # Examples /// @@ -64,7 +64,7 @@ impl AttachCommand { } } - /// Override the key sequence for detaching a container + /// Overrides the key sequence for detaching a container. /// /// # Examples /// @@ -80,7 +80,7 @@ impl AttachCommand { self } - /// Do not attach STDIN + /// Doesn't attach STDIN. /// /// # Examples /// @@ -96,7 +96,7 @@ impl AttachCommand { self } - /// Do not proxy signals + /// Doesn't proxy signals. /// /// # Examples /// @@ -112,13 +112,14 @@ impl AttachCommand { self } - /// Execute the attach command + /// Executes the attach command. /// /// # Errors + /// /// Returns an error if: - /// - The Docker daemon is not running - /// - The container doesn't exist - /// - The container is not running + /// - The Docker daemon is not running; + /// - The container doesn't exist; + /// - The container is not running. /// /// # Examples /// @@ -149,6 +150,10 @@ impl AttachCommand { impl DockerCommand for AttachCommand { type Output = CommandOutput; + fn command_name() -> &'static str { + "attach" + } + fn executor(&self) -> &CommandExecutor { &self.executor } @@ -173,10 +178,10 @@ impl DockerCommand for AttachCommand { args.push("--sig-proxy=false".to_string()); } - // Add container name/ID + // add container name/ID args.push(self.container.clone()); - // Add raw arguments from executor + // add raw arguments from executor args.extend(self.executor.raw_args.clone()); args @@ -188,23 +193,23 @@ impl DockerCommand for AttachCommand { } } -/// Result from the attach command +/// Result from the attach command. #[derive(Debug, Clone)] pub struct AttachResult { - /// Raw command output + /// Raw command output. pub output: CommandOutput, - /// Container that was attached to + /// Container that was attached to. pub container: String, } impl AttachResult { - /// Check if the attach was successful + /// Checks if the attach was successful. #[must_use] pub fn success(&self) -> bool { self.output.success } - /// Get the container name + /// Gets the container name. #[must_use] pub fn container(&self) -> &str { &self.container diff --git a/src/command/bake.rs b/src/command/bake.rs index 26240754..7468dc46 100644 --- a/src/command/bake.rs +++ b/src/command/bake.rs @@ -1,4 +1,4 @@ -//! Docker Bake Command Implementation +//! Docker bake command implementation. //! //! This module provides a comprehensive implementation of the `docker bake` command, //! supporting all native Docker buildx bake options for building from configuration files. @@ -13,7 +13,7 @@ //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { -//! // Basic bake with default docker-bake.hcl file +//! // basic bake with default docker-bake.hcl file //! let bake_cmd = BakeCommand::new(); //! let output = bake_cmd.execute().await?; //! println!("Bake completed: {}", output.success); @@ -29,7 +29,7 @@ //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { -//! // Advanced bake with custom file and targets +//! // advanced bake with custom file and targets //! let bake_cmd = BakeCommand::new() //! .file("docker-compose.yml") //! .file("custom-bake.hcl") @@ -51,7 +51,7 @@ use crate::error::Result; use async_trait::async_trait; use std::collections::HashMap; -/// Docker Bake Command Builder +/// Docker bake command builder. /// /// Implements the `docker bake` command for building from configuration files /// like docker-compose.yml, docker-bake.hcl, or custom bake definitions. @@ -60,18 +60,18 @@ use std::collections::HashMap; /// /// The bake command allows you to build multiple targets defined in configuration /// files, supporting advanced features like: -/// - Multi-platform builds -/// - Build matrix configurations -/// - Shared build contexts -/// - Variable substitution -/// - Target dependencies +/// - Multi-platform builds; +/// - Build matrix configurations; +/// - Shared build contexts; +/// - Variable substitution; +/// - Target dependencies. /// /// # Supported File Formats /// -/// - `docker-compose.yml` - Docker Compose service definitions -/// - `docker-bake.hcl` - HCL (`HashiCorp` Configuration Language) format -/// - `docker-bake.json` - JSON format -/// - Custom build definition files +/// - `docker-compose.yml` - Docker Compose service definitions; +/// - `docker-bake.hcl` - HCL (`HashiCorp` Configuration Language) format; +/// - `docker-bake.json` - JSON format; +/// - Custom build definition files. /// /// # Examples /// @@ -81,7 +81,7 @@ use std::collections::HashMap; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { -/// // Build all targets from docker-compose.yml +/// // builds all targets from docker-compose.yml /// let output = BakeCommand::new() /// .file("docker-compose.yml") /// .execute() @@ -94,48 +94,100 @@ use std::collections::HashMap; #[derive(Debug, Clone)] #[allow(clippy::struct_excessive_bools)] pub struct BakeCommand { - /// Build targets to build (defaults to all targets if empty) + /// Build targets to build (defaults to all targets if empty). targets: Vec, - /// Build definition files + /// Build definition files. files: Vec, - /// Resource access permissions + /// Resource access permissions. allow: Vec, - /// Builder instance override + /// Builder instance override. builder: Option, - /// Evaluation method (build, check, outline, targets) - call: Option, - /// Enable check mode (shorthand for --call=check) + /// Evaluation method to call. + call: Option, + /// Enables check mode (shorthand for `--call=check`). check: bool, - /// Enable debug logging + /// Enables debug logging. debug: bool, - /// List targets or variables + /// Lists targets or variables. list: Option, - /// Load images to Docker daemon (shorthand for --set=*.output=type=docker) + /// Loads images to Docker daemon (shorthand for `--set=*.output=type=docker`). load: bool, - /// Build result metadata file + /// Build result metadata file. metadata_file: Option, - /// Disable build cache + /// Disables build cache. no_cache: bool, - /// Print options without building + /// Prints options without building. print: bool, - /// Progress output type - progress: Option, - /// Provenance attestation (shorthand for --set=*.attest=type=provenance) + /// Progress output type. + progress: Option, + /// Provenance attestation (shorthand for `--set=*.attest=type=provenance`). provenance: Option, - /// Always pull referenced images + /// Always pulls referenced images. pull: bool, - /// Push images to registry (shorthand for --set=*.output=type=registry) + /// Pushes images to registry (shorthand for `--set=*.output=type=registry`). push: bool, - /// SBOM attestation (shorthand for --set=*.attest=type=sbom) + /// SBOM attestation (shorthand for `--set=*.attest=type=sbom`). sbom: Option, - /// Target value overrides (key=value pairs) + /// Target value overrides (key=value pairs). set_values: HashMap, - /// Command executor for handling raw arguments and execution + /// Command executor for handling raw arguments and execution. pub executor: CommandExecutor, } +/// Evaluation methods for the bake command. +#[derive(Debug, Clone)] +pub enum EvaluationMethod { + /// Builds the specified targets. + Build, + /// Validates the build configuration without executing. + Check, + /// Outlines the build plan without executing. + Outline, + /// Lists available build targets. + Targets, +} + +impl std::fmt::Display for EvaluationMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EvaluationMethod::Build => write!(f, "build"), + EvaluationMethod::Check => write!(f, "check"), + EvaluationMethod::Outline => write!(f, "outline"), + EvaluationMethod::Targets => write!(f, "targets"), + } + } +} + +/// Progress output types for the bake command. +#[derive(Debug, Default, Clone)] +pub enum ProgressType { + /// Auto-detects progress output type. + #[default] + Auto, + /// Quiet output. + Quiet, + /// Plain text output. + Plain, + /// TTY output. + Tty, + /// Raw JSON output. + RawJson, +} + +impl std::fmt::Display for ProgressType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ProgressType::Auto => write!(f, "auto"), + ProgressType::Quiet => write!(f, "quiet"), + ProgressType::Plain => write!(f, "plain"), + ProgressType::Tty => write!(f, "tty"), + ProgressType::RawJson => write!(f, "rawjson"), + } + } +} + impl BakeCommand { - /// Create a new `BakeCommand` instance + /// Creates a new [`BakeCommand`]. /// /// # Examples /// @@ -190,7 +242,7 @@ impl BakeCommand { self } - /// Add multiple targets to build + /// Adds multiple targets to build. /// /// # Examples /// @@ -211,7 +263,7 @@ impl BakeCommand { self } - /// Add a build definition file + /// Adds a build definition file. /// /// Supports docker-compose.yml, docker-bake.hcl, docker-bake.json, /// and custom build definition files. @@ -231,7 +283,7 @@ impl BakeCommand { self } - /// Add multiple build definition files + /// Adds multiple build definition files. /// /// # Examples /// @@ -252,7 +304,7 @@ impl BakeCommand { self } - /// Allow build to access specified resources + /// Allows build to access specified resources. /// /// Grants permission to access host resources during build. /// @@ -271,75 +323,37 @@ impl BakeCommand { self } - /// Override the configured builder instance - /// - /// # Examples - /// - /// ``` - /// use docker_wrapper::BakeCommand; - /// - /// let bake_cmd = BakeCommand::new() - /// .builder("mybuilder"); - /// ``` + /// Overrides the configured builder instance. #[must_use] pub fn builder>(mut self, builder: S) -> Self { self.builder = Some(builder.into()); self } - /// Set method for evaluating build - /// - /// Valid values: "build", "check", "outline", "targets" - /// - /// # Examples - /// - /// ``` - /// use docker_wrapper::BakeCommand; - /// - /// let bake_cmd = BakeCommand::new() - /// .call("check"); // Validate build configuration - /// ``` + /// Sets method for evaluating build. #[must_use] - pub fn call>(mut self, method: S) -> Self { - self.call = Some(method.into()); + pub fn call(mut self, method: EvaluationMethod) -> Self { + self.call = Some(method); self } - /// Enable check mode (shorthand for --call=check) + /// Enables check mode (shorthand for `--call=check`). /// /// Validates the build configuration without executing the build. - /// - /// # Examples - /// - /// ``` - /// use docker_wrapper::BakeCommand; - /// - /// let bake_cmd = BakeCommand::new() - /// .check(); - /// ``` #[must_use] pub fn check(mut self) -> Self { self.check = true; self } - /// Enable debug logging - /// - /// # Examples - /// - /// ``` - /// use docker_wrapper::BakeCommand; - /// - /// let bake_cmd = BakeCommand::new() - /// .debug(); - /// ``` + /// Enables debug logging. #[must_use] pub fn debug(mut self) -> Self { self.debug = true; self } - /// List targets or variables + /// Lists targets or variables. /// /// # Examples /// @@ -347,7 +361,7 @@ impl BakeCommand { /// use docker_wrapper::BakeCommand; /// /// let bake_cmd = BakeCommand::new() - /// .list("targets"); // List all available targets + /// .list("targets"); // lists all available targets /// ``` #[must_use] pub fn list>(mut self, list_type: S) -> Self { @@ -355,7 +369,7 @@ impl BakeCommand { self } - /// Load images to Docker daemon (shorthand for --set=*.output=type=docker) + /// Loads images to Docker daemon (shorthand for `--set=*.output=type=docker`). /// /// # Examples /// @@ -371,7 +385,7 @@ impl BakeCommand { self } - /// Write build result metadata to a file + /// Writes build result metadata to a file. /// /// # Examples /// @@ -387,7 +401,7 @@ impl BakeCommand { self } - /// Do not use cache when building images + /// Doesn't use cache when building images. /// /// # Examples /// @@ -403,7 +417,7 @@ impl BakeCommand { self } - /// Print the options without building + /// Prints the options without building. /// /// # Examples /// @@ -419,9 +433,9 @@ impl BakeCommand { self } - /// Set type of progress output + /// Sets type of progress output. /// - /// Valid values: "auto", "quiet", "plain", "tty", "rawjson" + /// Valid values: `auto`, `quiet`, `plain`, `tty`, and `rawjson`. /// /// # Examples /// diff --git a/src/command/compose/attach.rs b/src/command/compose/attach.rs index 7e60e502..31789e59 100644 --- a/src/command/compose/attach.rs +++ b/src/command/compose/attach.rs @@ -18,7 +18,7 @@ pub struct ComposeAttachCommand { pub config: ComposeConfig, /// Service to attach to. pub service: String, - /// Detach keys sequence. + /// Overrides the key sequence for detaching. pub detach_keys: Option, /// Container index if service has multiple instances. pub index: Option, @@ -28,7 +28,7 @@ pub struct ComposeAttachCommand { pub sig_proxy: bool, } -/// Result from attach command. +/// Result from [`ComposeAttachCommand`]. #[derive(Debug, Clone)] pub struct AttachResult { /// Output from the command. @@ -38,7 +38,7 @@ pub struct AttachResult { } impl ComposeAttachCommand { - /// Creates a new attach command. + /// Creates a new [`ComposeAttachCommand`]. #[must_use] pub fn new(service: impl Into) -> Self { Self { @@ -50,7 +50,7 @@ impl ComposeAttachCommand { } } - /// Sets detach keys. + /// Overrides the key sequence for detaching. #[must_use] pub fn detach_keys(mut self, keys: impl Into) -> Self { self.detach_keys = Some(keys.into()); diff --git a/src/command/compose/build.rs b/src/command/compose/build.rs index 134d0918..bde8a0c9 100644 --- a/src/command/compose/build.rs +++ b/src/command/compose/build.rs @@ -31,14 +31,14 @@ pub struct ComposeBuildCommand { /// Amount of memory for builds. pub memory: Option, /// Builds with `BuildKit` progress output. - pub progress: Option, + pub progress: Option, /// Sets the SSH agent socket or key. pub ssh: Option, } /// Build progress output type. #[derive(Debug, Default, Clone, Copy)] -pub enum ProgressOutput { +pub enum ProgressType { /// Auto-detects progress type. #[default] Auto, @@ -48,7 +48,7 @@ pub enum ProgressOutput { Tty, } -impl std::fmt::Display for ProgressOutput { +impl std::fmt::Display for ProgressType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Auto => write!(f, "auto"), @@ -58,7 +58,7 @@ impl std::fmt::Display for ProgressOutput { } } -/// Result from compose build command. +/// Result from [`ComposeBuildCommand`]. #[derive(Debug, Clone)] pub struct ComposeBuildResult { /// Raw stdout output. @@ -72,7 +72,7 @@ pub struct ComposeBuildResult { } impl ComposeBuildCommand { - /// Creates a new compose build command. + /// Creates a new [`ComposeBuildCommand`]. #[must_use] pub fn new() -> Self { Self { @@ -159,7 +159,7 @@ impl ComposeBuildCommand { /// Sets progress output type. #[must_use] - pub fn progress(mut self, progress: ProgressOutput) -> Self { + pub fn progress(mut self, progress: ProgressType) -> Self { self.progress = Some(progress); self } @@ -349,7 +349,7 @@ mod tests { .parallel() .build_arg("VERSION", "2.0") .memory("1g") - .progress(ProgressOutput::Plain) + .progress(ProgressType::Plain) .ssh("default") .services(vec!["web", "worker"]); @@ -371,9 +371,9 @@ mod tests { #[test] fn test_progress_output_display() { - assert_eq!(ProgressOutput::Auto.to_string(), "auto"); - assert_eq!(ProgressOutput::Plain.to_string(), "plain"); - assert_eq!(ProgressOutput::Tty.to_string(), "tty"); + assert_eq!(ProgressType::Auto.to_string(), "auto"); + assert_eq!(ProgressType::Plain.to_string(), "plain"); + assert_eq!(ProgressType::Tty.to_string(), "tty"); } #[test] diff --git a/src/command/compose/config.rs b/src/command/compose/config.rs index 27bb45d7..aae34fdb 100644 --- a/src/command/compose/config.rs +++ b/src/command/compose/config.rs @@ -60,7 +60,7 @@ impl std::fmt::Display for ConfigFormat { } } -/// Result from compose config command. +/// Result from [`ComposeConfigCommand`]. #[derive(Debug, Clone)] pub struct ComposeConfigResult { /// Raw stdout output (configuration YAML/JSON). @@ -74,7 +74,7 @@ pub struct ComposeConfigResult { } impl ComposeConfigCommand { - /// Creates a new compose config command. + /// Creates a new [`ComposeConfigCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/convert.rs b/src/command/compose/convert.rs index 99d6bf2b..7ae69b74 100644 --- a/src/command/compose/convert.rs +++ b/src/command/compose/convert.rs @@ -39,7 +39,7 @@ impl std::fmt::Display for ConvertFormat { } } -/// Result from compose convert command. +/// Result from [`ComposeConvertCommand`]. #[derive(Debug, Clone)] pub struct ComposeConvertResult { /// Raw stdout output. @@ -53,7 +53,7 @@ pub struct ComposeConvertResult { } impl ComposeConvertCommand { - /// Creates a new compose convert command. + /// Creates a new [`ComposeConvertCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/cp.rs b/src/command/compose/cp.rs index 0f58daad..4c9b14a8 100644 --- a/src/command/compose/cp.rs +++ b/src/command/compose/cp.rs @@ -26,7 +26,7 @@ pub struct ComposeCpCommand { pub index: Option, } -/// Result from compose cp command. +/// Result from [`ComposeCpCommand`]. #[derive(Debug, Clone)] pub struct ComposeCpResult { /// Raw stdout output. @@ -42,7 +42,7 @@ pub struct ComposeCpResult { } impl ComposeCpCommand { - /// Creates a new compose cp command. + /// Creates a new [`ComposeCpCommand`]. #[must_use] pub fn new(source: impl Into, destination: impl Into) -> Self { Self { diff --git a/src/command/compose/create.rs b/src/command/compose/create.rs index 615b4c66..8f93e858 100644 --- a/src/command/compose/create.rs +++ b/src/command/compose/create.rs @@ -56,7 +56,7 @@ impl std::fmt::Display for PullPolicy { } } -/// Result from compose create command. +/// Result from [`ComposeCreateCommand`]. #[derive(Debug, Clone)] pub struct ComposeCreateResult { /// Raw stdout output. @@ -70,7 +70,7 @@ pub struct ComposeCreateResult { } impl ComposeCreateCommand { - /// Creates a new compose create command. + /// Creates a new [`ComposeCreateCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/down.rs b/src/command/compose/down.rs index 22fc9ca7..39f9a02c 100644 --- a/src/command/compose/down.rs +++ b/src/command/compose/down.rs @@ -45,7 +45,7 @@ impl std::fmt::Display for RemoveImages { } } -/// Result from compose down command. +/// Result from [`ComposeDownCommand`]. #[derive(Debug, Clone)] pub struct ComposeDownResult { /// Raw stdout output. @@ -61,7 +61,7 @@ pub struct ComposeDownResult { } impl ComposeDownCommand { - /// Creates a new compose down command. + /// Creates a new [`ComposeDownCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/events.rs b/src/command/compose/events.rs index dfc3dcd4..07a8e742 100644 --- a/src/command/compose/events.rs +++ b/src/command/compose/events.rs @@ -43,7 +43,7 @@ pub struct ComposeEvent { pub attributes: Option, } -/// Result from compose events command. +/// Result from [`ComposeEventsCommand`]. #[derive(Debug, Clone)] pub struct ComposeEventsResult { /// Raw stdout output. @@ -59,7 +59,7 @@ pub struct ComposeEventsResult { } impl ComposeEventsCommand { - /// Creates a new compose events command. + /// Creates a new [`ComposeEventsCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/exec.rs b/src/command/compose/exec.rs index cf5fd097..6de0af61 100644 --- a/src/command/compose/exec.rs +++ b/src/command/compose/exec.rs @@ -38,7 +38,7 @@ pub struct ComposeExecCommand { pub privileged: bool, } -/// Result from compose exec command. +/// Result from [`ComposeExecCommand`]. #[derive(Debug, Clone)] pub struct ComposeExecResult { /// Raw stdout output. @@ -56,7 +56,7 @@ pub struct ComposeExecResult { } impl ComposeExecCommand { - /// Creates a new compose exec command. + /// Creates a new [`ComposeExecCommand`]. #[must_use] pub fn new(service: impl Into) -> Self { Self { diff --git a/src/command/compose/images.rs b/src/command/compose/images.rs index 551c8a8a..84eaef60 100644 --- a/src/command/compose/images.rs +++ b/src/command/compose/images.rs @@ -58,7 +58,7 @@ pub struct ImageInfo { pub size: String, } -/// Result from compose images command. +/// Result from [`ComposeImagesCommand`]. #[derive(Debug, Clone)] pub struct ComposeImagesResult { /// Raw stdout output. @@ -74,7 +74,7 @@ pub struct ComposeImagesResult { } impl ComposeImagesCommand { - /// Creates a new compose images command. + /// Creates a new [`ComposeImagesCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/kill.rs b/src/command/compose/kill.rs index c20d5770..d1c29384 100644 --- a/src/command/compose/kill.rs +++ b/src/command/compose/kill.rs @@ -20,7 +20,7 @@ pub struct ComposeKillCommand { pub signal: Option, } -/// Result from compose kill command. +/// Result from [`ComposeKillCommand`]. #[derive(Debug, Clone)] pub struct ComposeKillResult { /// Raw stdout output. @@ -34,7 +34,7 @@ pub struct ComposeKillResult { } impl ComposeKillCommand { - /// Creates a new compose kill command. + /// Creates a new [`ComposeKillCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/logs.rs b/src/command/compose/logs.rs index 1ddbbd14..760a14b5 100644 --- a/src/command/compose/logs.rs +++ b/src/command/compose/logs.rs @@ -33,7 +33,7 @@ pub struct ComposeLogsCommand { pub no_color: bool, } -/// Result from compose logs command. +/// Result from [`ComposeLogsCommand`]. #[derive(Debug, Clone)] pub struct ComposeLogsResult { /// Raw stdout output. @@ -47,7 +47,7 @@ pub struct ComposeLogsResult { } impl ComposeLogsCommand { - /// Creates a new compose logs command. + /// Creates a new [`ComposeLogsCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/ls.rs b/src/command/compose/ls.rs index cedf0b72..670c63d3 100644 --- a/src/command/compose/ls.rs +++ b/src/command/compose/ls.rs @@ -64,7 +64,7 @@ pub struct ComposeProject { pub created: String, } -/// Result from ls command. +/// Result from [`ComposeLsCommand`]. #[derive(Debug, Clone)] pub struct LsResult { /// List of compose projects. @@ -74,7 +74,7 @@ pub struct LsResult { } impl ComposeLsCommand { - /// Creates a new ls command. + /// Creates a new [`ComposeLsCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/pause.rs b/src/command/compose/pause.rs index 031dc785..5cf728d3 100644 --- a/src/command/compose/pause.rs +++ b/src/command/compose/pause.rs @@ -18,7 +18,7 @@ pub struct ComposePauseCommand { pub services: Vec, } -/// Result from compose pause command. +/// Result from [`ComposePauseCommand`]. #[derive(Debug, Clone)] pub struct ComposePauseResult { /// Raw stdout output. @@ -32,7 +32,7 @@ pub struct ComposePauseResult { } impl ComposePauseCommand { - /// Creates a new compose pause command. + /// Creates a new [`ComposePauseCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/port.rs b/src/command/compose/port.rs index e6075144..7a1e82ec 100644 --- a/src/command/compose/port.rs +++ b/src/command/compose/port.rs @@ -24,7 +24,7 @@ pub struct ComposePortCommand { pub index: Option, } -/// Result from compose port command. +/// Result from [`ComposePortCommand`]. #[derive(Debug, Clone)] pub struct ComposePortResult { /// Raw stdout output. @@ -40,7 +40,7 @@ pub struct ComposePortResult { } impl ComposePortCommand { - /// Creates a new compose port command. + /// Creates a new [`ComposePortCommand`]. #[must_use] pub fn new(service: impl Into) -> Self { Self { diff --git a/src/command/compose/ps.rs b/src/command/compose/ps.rs index 1fec3c62..c9835756 100644 --- a/src/command/compose/ps.rs +++ b/src/command/compose/ps.rs @@ -95,7 +95,7 @@ pub struct PortPublisher { pub protocol: String, } -/// Result from compose ps command. +/// Result from [`ComposePsCommand`]. #[derive(Debug, Clone)] pub struct ComposePsResult { /// Raw stdout output. @@ -109,7 +109,7 @@ pub struct ComposePsResult { } impl ComposePsCommand { - /// Creates a new compose ps command. + /// Creates a new [`ComposePsCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/push.rs b/src/command/compose/push.rs index e8bd76a1..27b0f633 100644 --- a/src/command/compose/push.rs +++ b/src/command/compose/push.rs @@ -24,7 +24,7 @@ pub struct ComposePushCommand { pub quiet: bool, } -/// Result from compose push command. +/// Result from [`ComposePushCommand`]. #[derive(Debug, Clone)] pub struct ComposePushResult { /// Raw stdout output. @@ -38,7 +38,7 @@ pub struct ComposePushResult { } impl ComposePushCommand { - /// Creates a new compose push command. + /// Creates a new [`ComposePushCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/restart.rs b/src/command/compose/restart.rs index bc4c5793..b1c58710 100644 --- a/src/command/compose/restart.rs +++ b/src/command/compose/restart.rs @@ -21,7 +21,7 @@ pub struct ComposeRestartCommand { pub timeout: Option, } -/// Result from compose restart command. +/// Result from [`ComposeRestartCommand`]. #[derive(Debug, Clone)] pub struct ComposeRestartResult { /// Raw stdout output. @@ -35,7 +35,7 @@ pub struct ComposeRestartResult { } impl ComposeRestartCommand { - /// Creates a new compose restart command. + /// Creates a new [`ComposeRestartCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/rm.rs b/src/command/compose/rm.rs index 75722d84..e6a87e90 100644 --- a/src/command/compose/rm.rs +++ b/src/command/compose/rm.rs @@ -25,7 +25,7 @@ pub struct ComposeRmCommand { pub volumes: bool, } -/// Result from compose rm command. +/// Result from [`ComposeRmCommand`]. #[derive(Debug, Clone)] pub struct ComposeRmResult { /// Raw stdout output. @@ -41,7 +41,7 @@ pub struct ComposeRmResult { } impl ComposeRmCommand { - /// Creates a new compose rm command. + /// Creates a new [`ComposeRmCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/run.rs b/src/command/compose/run.rs index 178658a9..5f1b5850 100644 --- a/src/command/compose/run.rs +++ b/src/command/compose/run.rs @@ -50,7 +50,7 @@ pub struct ComposeRunCommand { pub volume_rm: bool, } -/// Result from compose run command. +/// Result from [`ComposeRunCommand`]. #[derive(Debug, Clone)] pub struct ComposeRunResult { /// Raw stdout output. @@ -68,7 +68,7 @@ pub struct ComposeRunResult { } impl ComposeRunCommand { - /// Creates a new compose run command. + /// Creates a new [`ComposeRunCommand`]. #[must_use] pub fn new(service: impl Into) -> Self { Self { @@ -470,11 +470,11 @@ mod tests { let args = cmd.build_subcommand_args(); - // check flags + // checks flags assert!(args.contains(&"--detach".to_string())); assert!(args.contains(&"--rm".to_string())); - // check named parameters + // checks named parameters assert!(args.contains(&"--name".to_string())); assert!(args.contains(&"test-db".to_string())); assert!(args.contains(&"--user".to_string())); @@ -488,11 +488,11 @@ mod tests { assert!(args.contains(&"--entrypoint".to_string())); assert!(args.contains(&"docker-entrypoint.sh".to_string())); - // check service and command + // checks service and command assert!(args.contains(&"database".to_string())); assert!(args.contains(&"postgres".to_string())); - // check env and labels + // checks env and labels assert!(args.contains(&"POSTGRES_DB=testdb".to_string())); assert!(args.contains(&"env=test".to_string())); } diff --git a/src/command/compose/scale.rs b/src/command/compose/scale.rs index 9d36f96d..d91a8aa3 100644 --- a/src/command/compose/scale.rs +++ b/src/command/compose/scale.rs @@ -21,7 +21,7 @@ pub struct ComposeScaleCommand { pub no_deps: bool, } -/// Result from compose scale command. +/// Result from [`ComposeScaleCommand`]. #[derive(Debug, Clone)] pub struct ComposeScaleResult { /// Raw stdout output. @@ -35,7 +35,7 @@ pub struct ComposeScaleResult { } impl ComposeScaleCommand { - /// Creates a new compose scale command. + /// Creates a new [`ComposeScaleCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/start.rs b/src/command/compose/start.rs index 045bcb8d..014f1369 100644 --- a/src/command/compose/start.rs +++ b/src/command/compose/start.rs @@ -18,7 +18,7 @@ pub struct ComposeStartCommand { pub services: Vec, } -/// Result from compose start command. +/// Result from [`ComposeStartCommand`]. #[derive(Debug, Clone)] pub struct ComposeStartResult { /// Raw stdout output. @@ -32,7 +32,7 @@ pub struct ComposeStartResult { } impl ComposeStartCommand { - /// Creates a new compose start command. + /// Creates a new [`ComposeStartCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/stop.rs b/src/command/compose/stop.rs index 6f597857..86e7ae93 100644 --- a/src/command/compose/stop.rs +++ b/src/command/compose/stop.rs @@ -21,7 +21,7 @@ pub struct ComposeStopCommand { pub timeout: Option, } -/// Result from compose stop command. +/// Result from [`ComposeStopCommand`]. #[derive(Debug, Clone)] pub struct ComposeStopResult { /// Raw stdout output. @@ -35,7 +35,7 @@ pub struct ComposeStopResult { } impl ComposeStopCommand { - /// Creates a new compose stop command. + /// Creates a new [`ComposeStopCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/top.rs b/src/command/compose/top.rs index a64a8777..98436e7f 100644 --- a/src/command/compose/top.rs +++ b/src/command/compose/top.rs @@ -18,7 +18,7 @@ pub struct ComposeTopCommand { pub services: Vec, } -/// Result from compose top command. +/// Result from [`ComposeTopCommand`]. #[derive(Debug, Clone)] pub struct ComposeTopResult { /// Raw stdout output. @@ -32,7 +32,7 @@ pub struct ComposeTopResult { } impl ComposeTopCommand { - /// Creates a new compose top command. + /// Creates a new [`ComposeTopCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/unpause.rs b/src/command/compose/unpause.rs index 1260f13c..c1e0c0c9 100644 --- a/src/command/compose/unpause.rs +++ b/src/command/compose/unpause.rs @@ -18,7 +18,7 @@ pub struct ComposeUnpauseCommand { pub services: Vec, } -/// Result from compose unpause command. +/// Result from [`ComposeUnpauseCommand`]. #[derive(Debug, Clone)] pub struct ComposeUnpauseResult { /// Raw stdout output. @@ -32,7 +32,7 @@ pub struct ComposeUnpauseResult { } impl ComposeUnpauseCommand { - /// Creates a new compose unpause command. + /// Creates a new [`ComposeUnpauseCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/up.rs b/src/command/compose/up.rs index 041c528b..f28a04c0 100644 --- a/src/command/compose/up.rs +++ b/src/command/compose/up.rs @@ -78,7 +78,7 @@ impl std::fmt::Display for PullPolicy { } } -/// Result from compose up command. +/// Result from [`ComposeUpCommand`]. #[derive(Debug, Clone)] pub struct ComposeUpResult { /// Raw stdout output. @@ -94,7 +94,7 @@ pub struct ComposeUpResult { } impl ComposeUpCommand { - /// Creates a new compose up command. + /// Creates a new [`ComposeUpCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/version.rs b/src/command/compose/version.rs index a7b620b8..777db099 100644 --- a/src/command/compose/version.rs +++ b/src/command/compose/version.rs @@ -48,7 +48,7 @@ pub struct VersionInfo { pub version: String, } -/// Result from compose version command. +/// Result from [`ComposeVersionCommand`]. #[derive(Debug, Clone)] pub struct ComposeVersionResult { /// Raw stdout output. @@ -62,7 +62,7 @@ pub struct ComposeVersionResult { } impl ComposeVersionCommand { - /// Creates a new compose version command. + /// Creates a new [`ComposeVersionCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/wait.rs b/src/command/compose/wait.rs index e452af78..44978097 100644 --- a/src/command/compose/wait.rs +++ b/src/command/compose/wait.rs @@ -23,7 +23,7 @@ pub struct ComposeWaitCommand { pub wait_for_healthy: bool, } -/// Result from compose wait command. +/// Result from [`ComposeWaitCommand`]. #[derive(Debug, Clone)] pub struct ComposeWaitResult { /// Raw stdout output. @@ -39,7 +39,7 @@ pub struct ComposeWaitResult { } impl ComposeWaitCommand { - /// Creates a new compose wait command. + /// Creates a new [`ComposeWaitCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/command/compose/watch.rs b/src/command/compose/watch.rs index cc779d4f..d02e5237 100644 --- a/src/command/compose/watch.rs +++ b/src/command/compose/watch.rs @@ -22,7 +22,7 @@ pub struct ComposeWatchCommand { pub quiet: bool, } -/// Result from compose watch command. +/// Result from [`ComposeWatchCommand`]. #[derive(Debug, Clone)] pub struct ComposeWatchResult { /// Raw stdout output. @@ -36,7 +36,7 @@ pub struct ComposeWatchResult { } impl ComposeWatchCommand { - /// Creates a new compose watch command. + /// Creates a new [`ComposeWatchCommand`]. #[must_use] pub fn new() -> Self { Self { diff --git a/src/compose.rs b/src/compose.rs index 3ac77e62..07fe1143 100644 --- a/src/compose.rs +++ b/src/compose.rs @@ -194,13 +194,13 @@ pub trait ComposeCommand: DockerCommand { /// Builds complete command arguments including subcommand name and global args. fn build_command_args(&self) -> Vec { - let mut args = vec![Self::subcommand_name().to_string()]; + let mut args = Vec::new(); // add global compose arguments args.extend(self.config().build_global_args()); // add the subcommand - args.push(self.subcommand().to_string()); + args.push(Self::subcommand_name().to_string()); // add command-specific arguments args.extend(self.build_subcommand_args());