docker_wrapper/command/compose/
cp.rs1use crate::command::{CommandExecutor, ComposeCommand, ComposeConfig, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone)]
9pub struct ComposeCpCommand {
10 pub executor: CommandExecutor,
12 pub config: ComposeConfig,
14 pub source: String,
16 pub destination: String,
18 pub archive: bool,
20 pub follow_link: bool,
22 pub index: Option<u32>,
24}
25
26#[derive(Debug, Clone)]
28pub struct ComposeCpResult {
29 pub stdout: String,
31 pub stderr: String,
33 pub success: bool,
35 pub source: String,
37 pub destination: String,
39}
40
41impl ComposeCpCommand {
42 #[must_use]
44 pub fn new(source: impl Into<String>, destination: impl Into<String>) -> Self {
45 Self {
46 executor: CommandExecutor::new(),
47 config: ComposeConfig::new(),
48 source: source.into(),
49 destination: destination.into(),
50 archive: false,
51 follow_link: false,
52 index: None,
53 }
54 }
55
56 #[must_use]
58 pub fn archive(mut self) -> Self {
59 self.archive = true;
60 self
61 }
62
63 #[must_use]
65 pub fn follow_link(mut self) -> Self {
66 self.follow_link = true;
67 self
68 }
69
70 #[must_use]
72 pub fn index(mut self, index: u32) -> Self {
73 self.index = Some(index);
74 self
75 }
76}
77
78#[async_trait]
79impl DockerCommand for ComposeCpCommand {
80 type Output = ComposeCpResult;
81
82 fn get_executor(&self) -> &CommandExecutor {
83 &self.executor
84 }
85
86 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
87 &mut self.executor
88 }
89
90 fn build_command_args(&self) -> Vec<String> {
91 <Self as ComposeCommand>::build_command_args(self)
92 }
93
94 async fn execute(&self) -> Result<Self::Output> {
95 let args = <Self as ComposeCommand>::build_command_args(self);
96 let output = self.execute_command(args).await?;
97
98 Ok(ComposeCpResult {
99 stdout: output.stdout,
100 stderr: output.stderr,
101 success: output.success,
102 source: self.source.clone(),
103 destination: self.destination.clone(),
104 })
105 }
106}
107
108impl ComposeCommand for ComposeCpCommand {
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 fn subcommand(&self) -> &'static str {
118 "cp"
119 }
120
121 fn build_subcommand_args(&self) -> Vec<String> {
122 let mut args = Vec::new();
123
124 if self.archive {
125 args.push("--archive".to_string());
126 }
127
128 if self.follow_link {
129 args.push("--follow-link".to_string());
130 }
131
132 if let Some(index) = self.index {
133 args.push("--index".to_string());
134 args.push(index.to_string());
135 }
136
137 args.push(self.source.clone());
138 args.push(self.destination.clone());
139
140 args
141 }
142}
143
144impl ComposeCpResult {
145 #[must_use]
147 pub fn success(&self) -> bool {
148 self.success
149 }
150
151 #[must_use]
153 pub fn source(&self) -> &str {
154 &self.source
155 }
156
157 #[must_use]
159 pub fn destination(&self) -> &str {
160 &self.destination
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_compose_cp_basic() {
170 let cmd = ComposeCpCommand::new("web:/app/config.json", "./config.json");
171 let args = cmd.build_subcommand_args();
172 assert!(args.contains(&"web:/app/config.json".to_string()));
173 assert!(args.contains(&"./config.json".to_string()));
174
175 let full_args = ComposeCommand::build_command_args(&cmd);
176 assert_eq!(full_args[0], "compose");
177 assert!(full_args.contains(&"cp".to_string()));
178 }
179
180 #[test]
181 fn test_compose_cp_with_options() {
182 let cmd = ComposeCpCommand::new("./data", "db:/var/lib/data")
183 .archive()
184 .follow_link()
185 .index(2);
186
187 let args = cmd.build_subcommand_args();
188 assert!(args.contains(&"--archive".to_string()));
189 assert!(args.contains(&"--follow-link".to_string()));
190 assert!(args.contains(&"--index".to_string()));
191 assert!(args.contains(&"2".to_string()));
192 }
193}