docker_wrapper/command/
tag.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::Result;
7use async_trait::async_trait;
8
9#[derive(Debug, Clone)]
32pub struct TagCommand {
33 source_image: String,
35 target_image: String,
37 pub executor: CommandExecutor,
39}
40
41impl TagCommand {
42 #[must_use]
56 pub fn new(source_image: impl Into<String>, target_image: impl Into<String>) -> Self {
57 Self {
58 source_image: source_image.into(),
59 target_image: target_image.into(),
60 executor: CommandExecutor::new(),
61 }
62 }
63
64 pub async fn run(&self) -> Result<TagResult> {
89 let output = self.execute().await?;
90 Ok(TagResult {
91 output,
92 source_image: self.source_image.clone(),
93 target_image: self.target_image.clone(),
94 })
95 }
96
97 #[must_use]
99 pub fn get_executor(&self) -> &CommandExecutor {
100 &self.executor
101 }
102
103 #[must_use]
105 pub fn get_executor_mut(&mut self) -> &mut CommandExecutor {
106 &mut self.executor
107 }
108}
109
110#[async_trait]
111impl DockerCommand for TagCommand {
112 type Output = CommandOutput;
113
114 fn get_executor(&self) -> &CommandExecutor {
115 &self.executor
116 }
117
118 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
119 &mut self.executor
120 }
121
122 fn build_command_args(&self) -> Vec<String> {
123 let mut args = vec!["tag".to_string()];
124 args.push(self.source_image.clone());
125 args.push(self.target_image.clone());
126 args.extend(self.executor.raw_args.clone());
128 args
129 }
130
131 async fn execute(&self) -> Result<Self::Output> {
132 let args = self.build_command_args();
133 self.executor.execute_command("docker", args).await
134 }
135}
136
137#[derive(Debug, Clone)]
139pub struct TagResult {
140 pub output: CommandOutput,
142 pub source_image: String,
144 pub target_image: String,
146}
147
148impl TagResult {
149 #[must_use]
151 pub fn success(&self) -> bool {
152 self.output.success
153 }
154
155 #[must_use]
157 pub fn source_image(&self) -> &str {
158 &self.source_image
159 }
160
161 #[must_use]
163 pub fn target_image(&self) -> &str {
164 &self.target_image
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_tag_basic() {
174 let cmd = TagCommand::new("alpine:latest", "my-alpine:latest");
175 let args = cmd.build_command_args();
176 assert_eq!(args, vec!["tag", "alpine:latest", "my-alpine:latest"]);
177 }
178
179 #[test]
180 fn test_tag_with_registry() {
181 let cmd = TagCommand::new("myapp:latest", "docker.io/myuser/myapp:v1.0.0");
182 let args = cmd.build_command_args();
183 assert_eq!(
184 args,
185 vec!["tag", "myapp:latest", "docker.io/myuser/myapp:v1.0.0"]
186 );
187 }
188
189 #[test]
190 fn test_tag_with_image_id() {
191 let cmd = TagCommand::new("sha256:abc123", "myimage:tagged");
192 let args = cmd.build_command_args();
193 assert_eq!(args, vec!["tag", "sha256:abc123", "myimage:tagged"]);
194 }
195
196 #[test]
197 fn test_tag_same_repo_different_tag() {
198 let cmd = TagCommand::new("nginx:1.21", "nginx:stable");
199 let args = cmd.build_command_args();
200 assert_eq!(args, vec!["tag", "nginx:1.21", "nginx:stable"]);
201 }
202}