docker_wrapper/compose/
cp.rs1use crate::compose::{ComposeCommandV2 as ComposeCommand, ComposeConfig};
4use crate::error::Result;
5use async_trait::async_trait;
6use std::path::PathBuf;
7
8#[derive(Debug, Clone)]
12pub struct ComposeCpCommand {
13 pub config: ComposeConfig,
15 pub source: String,
17 pub destination: String,
19 pub archive: bool,
21 pub follow_link: bool,
23 pub index: Option<u32>,
25}
26
27#[derive(Debug, Clone)]
29pub struct CpResult {
30 pub output: String,
32 pub success: bool,
34}
35
36impl ComposeCpCommand {
37 #[must_use]
39 pub fn new(source: impl Into<String>, destination: impl Into<String>) -> Self {
40 Self {
41 config: ComposeConfig::default(),
42 source: source.into(),
43 destination: destination.into(),
44 archive: false,
45 follow_link: false,
46 index: None,
47 }
48 }
49
50 #[must_use]
52 pub fn from_container(
53 service: impl Into<String>,
54 container_path: impl Into<String>,
55 local_path: impl Into<PathBuf>,
56 ) -> Self {
57 let source = format!("{}:{}", service.into(), container_path.into());
58 let destination = local_path.into().display().to_string();
59 Self::new(source, destination)
60 }
61
62 #[must_use]
64 pub fn to_container(
65 local_path: impl Into<PathBuf>,
66 service: impl Into<String>,
67 container_path: impl Into<String>,
68 ) -> Self {
69 let source = local_path.into().display().to_string();
70 let destination = format!("{}:{}", service.into(), container_path.into());
71 Self::new(source, destination)
72 }
73
74 #[must_use]
76 pub fn file<P: Into<std::path::PathBuf>>(mut self, file: P) -> Self {
77 self.config.files.push(file.into());
78 self
79 }
80
81 #[must_use]
83 pub fn project_name(mut self, name: impl Into<String>) -> Self {
84 self.config.project_name = Some(name.into());
85 self
86 }
87
88 #[must_use]
90 pub fn archive(mut self) -> Self {
91 self.archive = true;
92 self
93 }
94
95 #[must_use]
97 pub fn follow_link(mut self) -> Self {
98 self.follow_link = true;
99 self
100 }
101
102 #[must_use]
104 pub fn index(mut self, index: u32) -> Self {
105 self.index = Some(index);
106 self
107 }
108
109 fn build_args(&self) -> Vec<String> {
110 let mut args = vec!["cp".to_string()];
111
112 if self.archive {
114 args.push("--archive".to_string());
115 }
116 if self.follow_link {
117 args.push("--follow-link".to_string());
118 }
119
120 if let Some(index) = self.index {
122 args.push("--index".to_string());
123 args.push(index.to_string());
124 }
125
126 args.push(self.source.clone());
128 args.push(self.destination.clone());
129
130 args
131 }
132}
133
134#[async_trait]
135impl ComposeCommand for ComposeCpCommand {
136 type Output = CpResult;
137
138 fn get_config(&self) -> &ComposeConfig {
139 &self.config
140 }
141
142 fn get_config_mut(&mut self) -> &mut ComposeConfig {
143 &mut self.config
144 }
145
146 async fn execute_compose(&self, args: Vec<String>) -> Result<Self::Output> {
147 let output = self.execute_compose_command(args).await?;
148
149 Ok(CpResult {
150 output: output.stdout,
151 success: output.success,
152 })
153 }
154
155 async fn execute(&self) -> Result<Self::Output> {
156 let args = self.build_args();
157 self.execute_compose(args).await
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_cp_command_basic() {
167 let cmd = ComposeCpCommand::new("web:/app/config.yml", "./config.yml");
168 let args = cmd.build_args();
169 assert_eq!(args[0], "cp");
170 assert!(args.contains(&"web:/app/config.yml".to_string()));
171 assert!(args.contains(&"./config.yml".to_string()));
172 }
173
174 #[test]
175 fn test_cp_from_container() {
176 let cmd = ComposeCpCommand::from_container("web", "/app/logs", "./logs");
177 let args = cmd.build_args();
178 assert!(args.contains(&"web:/app/logs".to_string()));
179 assert!(args.contains(&"./logs".to_string()));
180 }
181
182 #[test]
183 fn test_cp_to_container() {
184 let cmd = ComposeCpCommand::to_container("./config.yml", "web", "/app/config.yml");
185 let args = cmd.build_args();
186 assert!(args.contains(&"./config.yml".to_string()));
187 assert!(args.contains(&"web:/app/config.yml".to_string()));
188 }
189
190 #[test]
191 fn test_cp_command_with_flags() {
192 let cmd = ComposeCpCommand::new("web:/data", "./data")
193 .archive()
194 .follow_link()
195 .index(1);
196 let args = cmd.build_args();
197 assert!(args.contains(&"--archive".to_string()));
198 assert!(args.contains(&"--follow-link".to_string()));
199 assert!(args.contains(&"--index".to_string()));
200 assert!(args.contains(&"1".to_string()));
201 }
202}