docker_wrapper/command/manifest/
create.rs

1//! Docker manifest create command implementation.
2
3use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Result of manifest create command
8#[derive(Debug, Clone)]
9pub struct ManifestCreateResult {
10    /// The manifest list name that was created
11    pub manifest_list: String,
12    /// Raw output from the command
13    pub output: String,
14    /// Whether the command succeeded
15    pub success: bool,
16}
17
18impl ManifestCreateResult {
19    /// Parse the manifest create output
20    fn parse(manifest_list: &str, output: &CommandOutput) -> Self {
21        Self {
22            manifest_list: manifest_list.to_string(),
23            output: output.stdout.clone(),
24            success: output.success,
25        }
26    }
27}
28
29/// Docker manifest create command builder
30///
31/// Creates a local manifest list for annotating and pushing to a registry.
32///
33/// # Example
34///
35/// ```rust,no_run
36/// use docker_wrapper::{DockerCommand, ManifestCreateCommand};
37///
38/// # #[tokio::main]
39/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
40/// let result = ManifestCreateCommand::new("myapp:latest")
41///     .manifest("myapp:latest-amd64")
42///     .manifest("myapp:latest-arm64")
43///     .amend()
44///     .execute()
45///     .await?;
46///
47/// println!("Created manifest list: {}", result.manifest_list);
48/// # Ok(())
49/// # }
50/// ```
51#[derive(Debug, Clone)]
52pub struct ManifestCreateCommand {
53    /// The manifest list name
54    manifest_list: String,
55    /// Manifests to include in the list
56    manifests: Vec<String>,
57    /// Amend an existing manifest list
58    amend: bool,
59    /// Allow communication with an insecure registry
60    insecure: bool,
61    /// Command executor
62    pub executor: CommandExecutor,
63}
64
65impl ManifestCreateCommand {
66    /// Create a new manifest create command
67    ///
68    /// # Arguments
69    ///
70    /// * `manifest_list` - The name for the manifest list (e.g., "myapp:latest")
71    #[must_use]
72    pub fn new(manifest_list: impl Into<String>) -> Self {
73        Self {
74            manifest_list: manifest_list.into(),
75            manifests: Vec::new(),
76            amend: false,
77            insecure: false,
78            executor: CommandExecutor::new(),
79        }
80    }
81
82    /// Add a manifest to the list
83    ///
84    /// # Arguments
85    ///
86    /// * `manifest` - The manifest to add (e.g., "myapp:latest-amd64")
87    #[must_use]
88    pub fn manifest(mut self, manifest: impl Into<String>) -> Self {
89        self.manifests.push(manifest.into());
90        self
91    }
92
93    /// Add multiple manifests to the list
94    #[must_use]
95    pub fn manifests<I, S>(mut self, manifests: I) -> Self
96    where
97        I: IntoIterator<Item = S>,
98        S: Into<String>,
99    {
100        for manifest in manifests {
101            self.manifests.push(manifest.into());
102        }
103        self
104    }
105
106    /// Amend an existing manifest list
107    #[must_use]
108    pub fn amend(mut self) -> Self {
109        self.amend = true;
110        self
111    }
112
113    /// Allow communication with an insecure registry
114    #[must_use]
115    pub fn insecure(mut self) -> Self {
116        self.insecure = true;
117        self
118    }
119
120    /// Build the command arguments
121    fn build_args(&self) -> Vec<String> {
122        let mut args = vec!["manifest".to_string(), "create".to_string()];
123
124        if self.amend {
125            args.push("--amend".to_string());
126        }
127
128        if self.insecure {
129            args.push("--insecure".to_string());
130        }
131
132        args.push(self.manifest_list.clone());
133
134        for manifest in &self.manifests {
135            args.push(manifest.clone());
136        }
137
138        args.extend(self.executor.raw_args.clone());
139
140        args
141    }
142}
143
144#[async_trait]
145impl DockerCommand for ManifestCreateCommand {
146    type Output = ManifestCreateResult;
147
148    fn get_executor(&self) -> &CommandExecutor {
149        &self.executor
150    }
151
152    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
153        &mut self.executor
154    }
155
156    fn build_command_args(&self) -> Vec<String> {
157        self.build_args()
158    }
159
160    async fn execute(&self) -> Result<Self::Output> {
161        let args = self.build_args();
162        let output = self.execute_command(args).await?;
163        Ok(ManifestCreateResult::parse(&self.manifest_list, &output))
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn test_manifest_create_basic() {
173        let cmd = ManifestCreateCommand::new("myapp:latest");
174        let args = cmd.build_args();
175        assert_eq!(args, vec!["manifest", "create", "myapp:latest"]);
176    }
177
178    #[test]
179    fn test_manifest_create_with_manifests() {
180        let cmd = ManifestCreateCommand::new("myapp:latest")
181            .manifest("myapp:latest-amd64")
182            .manifest("myapp:latest-arm64");
183        let args = cmd.build_args();
184        assert_eq!(
185            args,
186            vec![
187                "manifest",
188                "create",
189                "myapp:latest",
190                "myapp:latest-amd64",
191                "myapp:latest-arm64"
192            ]
193        );
194    }
195
196    #[test]
197    fn test_manifest_create_with_amend() {
198        let cmd = ManifestCreateCommand::new("myapp:latest")
199            .manifest("myapp:latest-amd64")
200            .amend();
201        let args = cmd.build_args();
202        assert!(args.contains(&"--amend".to_string()));
203    }
204
205    #[test]
206    fn test_manifest_create_with_insecure() {
207        let cmd = ManifestCreateCommand::new("myapp:latest")
208            .manifest("myapp:latest-amd64")
209            .insecure();
210        let args = cmd.build_args();
211        assert!(args.contains(&"--insecure".to_string()));
212    }
213
214    #[test]
215    fn test_manifest_create_all_options() {
216        let cmd = ManifestCreateCommand::new("myapp:latest")
217            .manifests(vec!["myapp:latest-amd64", "myapp:latest-arm64"])
218            .amend()
219            .insecure();
220        let args = cmd.build_args();
221        assert!(args.contains(&"--amend".to_string()));
222        assert!(args.contains(&"--insecure".to_string()));
223        assert!(args.contains(&"myapp:latest-amd64".to_string()));
224        assert!(args.contains(&"myapp:latest-arm64".to_string()));
225    }
226}