docker_wrapper/compose/
stop.rs

1//! Docker Compose stop command implementation.
2
3use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput};
4use crate::error::Result;
5use async_trait::async_trait;
6use std::time::Duration;
7
8/// Docker Compose stop command builder
9#[derive(Debug, Clone, Default)]
10pub struct ComposeStopCommand {
11    config: ComposeConfig,
12    services: Vec<String>,
13    timeout: Option<Duration>,
14}
15
16impl ComposeStopCommand {
17    /// Create a new compose stop command
18    #[must_use]
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Create with a specific configuration
24    #[must_use]
25    pub fn with_config(config: ComposeConfig) -> Self {
26        Self {
27            config,
28            ..Default::default()
29        }
30    }
31
32    /// Add a service to stop
33    #[must_use]
34    pub fn service(mut self, service: impl Into<String>) -> Self {
35        self.services.push(service.into());
36        self
37    }
38
39    /// Set the timeout for stopping containers
40    #[must_use]
41    pub fn timeout(mut self, timeout: Duration) -> Self {
42        self.timeout = Some(timeout);
43        self
44    }
45
46    /// Execute the stop command
47    ///
48    /// # Errors
49    ///
50    /// Returns an error if the docker compose stop command fails
51    pub async fn run(&self) -> Result<ComposeOutput> {
52        self.execute().await
53    }
54}
55
56#[async_trait]
57impl ComposeCommand for ComposeStopCommand {
58    type Output = ComposeOutput;
59
60    fn subcommand(&self) -> &'static str {
61        "stop"
62    }
63
64    fn build_args(&self) -> Vec<String> {
65        let mut args = Vec::new();
66        if let Some(timeout) = self.timeout {
67            args.push("--timeout".to_string());
68            args.push(timeout.as_secs().to_string());
69        }
70        args.extend(self.services.clone());
71        args
72    }
73
74    async fn execute(&self) -> Result<Self::Output> {
75        execute_compose_command(&self.config, self.subcommand(), self.build_args()).await
76    }
77
78    fn config(&self) -> &ComposeConfig {
79        &self.config
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_compose_stop_basic() {
89        let cmd = ComposeStopCommand::new();
90        assert_eq!(cmd.subcommand(), "stop");
91        assert_eq!(cmd.build_args(), Vec::<String>::new());
92    }
93
94    #[test]
95    fn test_compose_stop_with_services() {
96        let cmd = ComposeStopCommand::new().service("web").service("worker");
97
98        let args = cmd.build_args();
99        assert_eq!(args, vec!["web", "worker"]);
100    }
101
102    #[test]
103    fn test_compose_stop_with_timeout() {
104        let cmd = ComposeStopCommand::new()
105            .timeout(Duration::from_secs(10))
106            .service("db");
107
108        let args = cmd.build_args();
109        assert_eq!(args, vec!["--timeout", "10", "db"]);
110    }
111
112    #[test]
113    fn test_compose_stop_with_config() {
114        let config = ComposeConfig::new()
115            .file("compose.yml")
116            .project_name("webapp");
117
118        let cmd = ComposeStopCommand::with_config(config)
119            .timeout(Duration::from_secs(5))
120            .service("redis")
121            .service("postgres");
122
123        assert_eq!(cmd.config().project_name, Some("webapp".to_string()));
124        let args = cmd.build_args();
125        assert_eq!(args, vec!["--timeout", "5", "redis", "postgres"]);
126    }
127
128    #[test]
129    fn test_compose_stop_zero_timeout() {
130        let cmd = ComposeStopCommand::new()
131            .timeout(Duration::from_secs(0))
132            .service("app");
133
134        let args = cmd.build_args();
135        assert_eq!(args, vec!["--timeout", "0", "app"]);
136    }
137
138    #[test]
139    fn test_compose_stop_long_timeout() {
140        let cmd = ComposeStopCommand::new()
141            .timeout(Duration::from_secs(300))
142            .service("slow-service");
143
144        let args = cmd.build_args();
145        assert_eq!(args, vec!["--timeout", "300", "slow-service"]);
146    }
147
148    #[test]
149    fn test_compose_stop_all_services() {
150        // Stop all services when no specific services are specified
151        let cmd = ComposeStopCommand::new().timeout(Duration::from_secs(30));
152
153        let args = cmd.build_args();
154        assert_eq!(args, vec!["--timeout", "30"]);
155    }
156}