docker_wrapper/compose/
start.rs

1//! Docker Compose start command implementation.
2
3use super::{execute_compose_command, ComposeCommand, ComposeConfig, ComposeOutput};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Docker Compose start command builder
8#[derive(Debug, Clone, Default)]
9pub struct ComposeStartCommand {
10    config: ComposeConfig,
11    services: Vec<String>,
12}
13
14impl ComposeStartCommand {
15    /// Create a new compose start command
16    #[must_use]
17    pub fn new() -> Self {
18        Self::default()
19    }
20
21    /// Create with a specific configuration
22    #[must_use]
23    pub fn with_config(config: ComposeConfig) -> Self {
24        Self {
25            config,
26            ..Default::default()
27        }
28    }
29
30    /// Add a service to start
31    #[must_use]
32    pub fn service(mut self, service: impl Into<String>) -> Self {
33        self.services.push(service.into());
34        self
35    }
36
37    /// Execute the start command
38    ///
39    /// # Errors
40    ///
41    /// Returns an error if the docker compose start command fails
42    pub async fn run(&self) -> Result<ComposeOutput> {
43        self.execute().await
44    }
45}
46
47#[async_trait]
48impl ComposeCommand for ComposeStartCommand {
49    type Output = ComposeOutput;
50
51    fn subcommand(&self) -> &'static str {
52        "start"
53    }
54
55    fn build_args(&self) -> Vec<String> {
56        self.services.clone()
57    }
58
59    async fn execute(&self) -> Result<Self::Output> {
60        execute_compose_command(&self.config, self.subcommand(), self.build_args()).await
61    }
62
63    fn config(&self) -> &ComposeConfig {
64        &self.config
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_compose_start_basic() {
74        let cmd = ComposeStartCommand::new();
75        assert_eq!(cmd.subcommand(), "start");
76        assert_eq!(cmd.build_args(), Vec::<String>::new());
77    }
78
79    #[test]
80    fn test_compose_start_with_services() {
81        let cmd = ComposeStartCommand::new()
82            .service("web")
83            .service("db")
84            .service("cache");
85
86        let args = cmd.build_args();
87        assert_eq!(args, vec!["web", "db", "cache"]);
88    }
89
90    #[test]
91    fn test_compose_start_with_config() {
92        let config = ComposeConfig::new()
93            .file("docker-compose.yml")
94            .project_name("myapp")
95            .profile("production");
96
97        let cmd = ComposeStartCommand::with_config(config).service("api");
98
99        assert_eq!(cmd.config().project_name, Some("myapp".to_string()));
100        assert_eq!(cmd.config().profiles, vec!["production"]);
101        assert_eq!(cmd.build_args(), vec!["api"]);
102    }
103
104    #[test]
105    fn test_compose_start_single_service() {
106        let cmd = ComposeStartCommand::new().service("postgres");
107
108        let args = cmd.build_args();
109        assert_eq!(args, vec!["postgres"]);
110    }
111
112    #[test]
113    fn test_compose_start_no_services() {
114        // Starting all services when no specific services are specified
115        let cmd = ComposeStartCommand::new();
116
117        let args = cmd.build_args();
118        assert!(args.is_empty());
119    }
120
121    #[test]
122    fn test_compose_start_builder_pattern() {
123        let cmd = ComposeStartCommand::with_config(ComposeConfig::new().project_name("test"))
124            .service("frontend")
125            .service("backend");
126
127        assert_eq!(cmd.services.len(), 2);
128        assert!(cmd.services.contains(&"frontend".to_string()));
129        assert!(cmd.services.contains(&"backend".to_string()));
130    }
131}