docker_wrapper/compose/
kill.rs1use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone, Default)]
11pub struct ComposeKillCommand {
12 pub config: ComposeConfig,
14 pub signal: Option<String>,
16 pub remove_orphans: bool,
18 pub services: Vec<String>,
20}
21
22#[derive(Debug, Clone)]
24pub struct KillResult {
25 pub output: String,
27 pub success: bool,
29}
30
31impl ComposeKillCommand {
32 #[must_use]
34 pub fn new() -> Self {
35 Self::default()
36 }
37
38 #[must_use]
40 pub fn file<P: Into<std::path::PathBuf>>(mut self, file: P) -> Self {
41 self.config.files.push(file.into());
42 self
43 }
44
45 #[must_use]
47 pub fn project_name(mut self, name: impl Into<String>) -> Self {
48 self.config.project_name = Some(name.into());
49 self
50 }
51
52 #[must_use]
54 pub fn signal(mut self, signal: impl Into<String>) -> Self {
55 self.signal = Some(signal.into());
56 self
57 }
58
59 #[must_use]
61 pub fn remove_orphans(mut self) -> Self {
62 self.remove_orphans = true;
63 self
64 }
65
66 #[must_use]
68 pub fn service(mut self, service: impl Into<String>) -> Self {
69 self.services.push(service.into());
70 self
71 }
72
73 #[must_use]
75 pub fn services<I, S>(mut self, services: I) -> Self
76 where
77 I: IntoIterator<Item = S>,
78 S: Into<String>,
79 {
80 self.services.extend(services.into_iter().map(Into::into));
81 self
82 }
83
84 fn build_args(&self) -> Vec<String> {
85 let mut args = vec!["kill".to_string()];
86
87 if let Some(signal) = &self.signal {
89 args.push("--signal".to_string());
90 args.push(signal.clone());
91 }
92
93 if self.remove_orphans {
95 args.push("--remove-orphans".to_string());
96 }
97
98 args.extend(self.services.clone());
100
101 args
102 }
103}
104
105#[async_trait]
106impl ComposeCommand for ComposeKillCommand {
107 type Output = KillResult;
108
109 fn get_config(&self) -> &ComposeConfig {
110 &self.config
111 }
112
113 fn get_config_mut(&mut self) -> &mut ComposeConfig {
114 &mut self.config
115 }
116
117 async fn execute_compose(&self, args: Vec<String>) -> Result<Self::Output> {
118 let output = self.execute_compose_command(args).await?;
119
120 Ok(KillResult {
121 output: output.stdout,
122 success: output.success,
123 })
124 }
125
126 async fn execute(&self) -> Result<Self::Output> {
127 let args = self.build_args();
128 self.execute_compose(args).await
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_kill_command_basic() {
138 let cmd = ComposeKillCommand::new();
139 let args = cmd.build_args();
140 assert_eq!(args[0], "kill");
141 }
142
143 #[test]
144 fn test_kill_command_with_signal() {
145 let cmd = ComposeKillCommand::new().signal("SIGTERM");
146 let args = cmd.build_args();
147 assert!(args.contains(&"--signal".to_string()));
148 assert!(args.contains(&"SIGTERM".to_string()));
149 }
150
151 #[test]
152 fn test_kill_command_with_services() {
153 let cmd = ComposeKillCommand::new()
154 .service("web")
155 .service("worker")
156 .remove_orphans();
157 let args = cmd.build_args();
158 assert!(args.contains(&"web".to_string()));
159 assert!(args.contains(&"worker".to_string()));
160 assert!(args.contains(&"--remove-orphans".to_string()));
161 }
162}