docker_wrapper/compose/
top.rs

1//! Docker Compose top command implementation.
2
3use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Docker Compose top command
8///
9/// Display running processes of a service.
10#[derive(Debug, Clone, Default)]
11pub struct ComposeTopCommand {
12    /// Base configuration
13    pub config: ComposeConfig,
14    /// Services to show processes for
15    pub services: Vec<String>,
16}
17
18/// Result from top command
19#[derive(Debug, Clone)]
20pub struct TopResult {
21    /// Output from the command (process list)
22    pub output: String,
23    /// Whether the operation succeeded
24    pub success: bool,
25}
26
27impl ComposeTopCommand {
28    /// Create a new top command
29    #[must_use]
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Add a compose file
35    #[must_use]
36    pub fn file<P: Into<std::path::PathBuf>>(mut self, file: P) -> Self {
37        self.config.files.push(file.into());
38        self
39    }
40
41    /// Set project name
42    #[must_use]
43    pub fn project_name(mut self, name: impl Into<String>) -> Self {
44        self.config.project_name = Some(name.into());
45        self
46    }
47
48    /// Add a service to show processes for
49    #[must_use]
50    pub fn service(mut self, service: impl Into<String>) -> Self {
51        self.services.push(service.into());
52        self
53    }
54
55    /// Add multiple services
56    #[must_use]
57    pub fn services<I, S>(mut self, services: I) -> Self
58    where
59        I: IntoIterator<Item = S>,
60        S: Into<String>,
61    {
62        self.services.extend(services.into_iter().map(Into::into));
63        self
64    }
65
66    fn build_args(&self) -> Vec<String> {
67        let mut args = vec!["top".to_string()];
68
69        // Add services
70        args.extend(self.services.clone());
71
72        args
73    }
74}
75
76#[async_trait]
77impl ComposeCommand for ComposeTopCommand {
78    type Output = TopResult;
79
80    fn get_config(&self) -> &ComposeConfig {
81        &self.config
82    }
83
84    fn get_config_mut(&mut self) -> &mut ComposeConfig {
85        &mut self.config
86    }
87
88    async fn execute_compose(&self, args: Vec<String>) -> Result<Self::Output> {
89        let output = self.execute_compose_command(args).await?;
90
91        Ok(TopResult {
92            output: output.stdout,
93            success: output.success,
94        })
95    }
96
97    async fn execute(&self) -> Result<Self::Output> {
98        let args = self.build_args();
99        self.execute_compose(args).await
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_top_command_basic() {
109        let cmd = ComposeTopCommand::new();
110        let args = cmd.build_args();
111        assert_eq!(args[0], "top");
112    }
113
114    #[test]
115    fn test_top_command_with_services() {
116        let cmd = ComposeTopCommand::new().service("web").service("worker");
117        let args = cmd.build_args();
118        assert!(args.contains(&"web".to_string()));
119        assert!(args.contains(&"worker".to_string()));
120    }
121}