docker_wrapper/command/
inspect.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
7use crate::error::Result;
8use async_trait::async_trait;
9use serde_json::Value;
10
11#[derive(Debug, Clone)]
31pub struct InspectCommand {
32 objects: Vec<String>,
34 format: Option<String>,
36 size: bool,
38 object_type: Option<String>,
40 pub executor: CommandExecutor,
42}
43
44impl InspectCommand {
45 #[must_use]
55 pub fn new(object: impl Into<String>) -> Self {
56 Self {
57 objects: vec![object.into()],
58 format: None,
59 size: false,
60 object_type: None,
61 executor: CommandExecutor::new(),
62 }
63 }
64
65 #[must_use]
75 pub fn new_multiple(objects: Vec<impl Into<String>>) -> Self {
76 Self {
77 objects: objects.into_iter().map(Into::into).collect(),
78 format: None,
79 size: false,
80 object_type: None,
81 executor: CommandExecutor::new(),
82 }
83 }
84
85 #[must_use]
87 pub fn object(mut self, object: impl Into<String>) -> Self {
88 self.objects.push(object.into());
89 self
90 }
91
92 #[must_use]
106 pub fn format(mut self, format: impl Into<String>) -> Self {
107 self.format = Some(format.into());
108 self
109 }
110
111 #[must_use]
113 pub fn size(mut self) -> Self {
114 self.size = true;
115 self
116 }
117
118 #[must_use]
122 pub fn object_type(mut self, typ: impl Into<String>) -> Self {
123 self.object_type = Some(typ.into());
124 self
125 }
126
127 pub async fn run(&self) -> Result<InspectOutput> {
135 let output = self.execute().await?;
136 Ok(InspectOutput { output })
137 }
138
139 #[must_use]
141 pub fn get_executor(&self) -> &CommandExecutor {
142 &self.executor
143 }
144
145 pub fn get_executor_mut(&mut self) -> &mut CommandExecutor {
147 &mut self.executor
148 }
149
150 #[must_use]
152 pub fn build_command_args(&self) -> Vec<String> {
153 let mut args = vec!["inspect".to_string()];
154
155 if let Some(ref format) = self.format {
156 args.push("--format".to_string());
157 args.push(format.clone());
158 }
159
160 if self.size {
161 args.push("--size".to_string());
162 }
163
164 if let Some(ref typ) = self.object_type {
165 args.push("--type".to_string());
166 args.push(typ.clone());
167 }
168
169 args.extend(self.objects.clone());
171
172 args.extend(self.executor.raw_args.clone());
174
175 args
176 }
177}
178
179#[async_trait]
180impl DockerCommand for InspectCommand {
181 type Output = CommandOutput;
182
183 fn get_executor(&self) -> &CommandExecutor {
184 &self.executor
185 }
186
187 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
188 &mut self.executor
189 }
190
191 fn build_command_args(&self) -> Vec<String> {
192 self.build_command_args()
193 }
194
195 async fn execute(&self) -> Result<Self::Output> {
196 let args = self.build_command_args();
197 self.execute_command(args).await
198 }
199}
200
201#[derive(Debug, Clone)]
203pub struct InspectOutput {
204 pub output: CommandOutput,
206}
207
208impl InspectOutput {
209 pub fn json(&self) -> Result<Value> {
226 serde_json::from_str(&self.output.stdout)
227 .map_err(|e| crate::error::Error::parse_error(format!("Failed to parse JSON: {e}")))
228 }
229
230 #[must_use]
232 pub fn stdout(&self) -> &str {
233 &self.output.stdout
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_inspect_single_object() {
243 let cmd = InspectCommand::new("test-container");
244 let args = cmd.build_command_args();
245 assert_eq!(args, vec!["inspect", "test-container"]);
246 }
247
248 #[test]
249 fn test_inspect_multiple_objects() {
250 let cmd = InspectCommand::new_multiple(vec!["container1", "image1", "volume1"]);
251 let args = cmd.build_command_args();
252 assert_eq!(args, vec!["inspect", "container1", "image1", "volume1"]);
253 }
254
255 #[test]
256 fn test_inspect_with_format() {
257 let cmd = InspectCommand::new("test-container").format("{{.State.Status}}");
258 let args = cmd.build_command_args();
259 assert_eq!(
260 args,
261 vec!["inspect", "--format", "{{.State.Status}}", "test-container"]
262 );
263 }
264
265 #[test]
266 fn test_inspect_with_size() {
267 let cmd = InspectCommand::new("test-image").size();
268 let args = cmd.build_command_args();
269 assert_eq!(args, vec!["inspect", "--size", "test-image"]);
270 }
271
272 #[test]
273 fn test_inspect_with_type() {
274 let cmd = InspectCommand::new("my-network").object_type("network");
275 let args = cmd.build_command_args();
276 assert_eq!(args, vec!["inspect", "--type", "network", "my-network"]);
277 }
278
279 #[test]
280 fn test_inspect_all_options() {
281 let cmd = InspectCommand::new("test-container")
282 .format("{{json .}}")
283 .size()
284 .object_type("container");
285 let args = cmd.build_command_args();
286 assert_eq!(
287 args,
288 vec![
289 "inspect",
290 "--format",
291 "{{json .}}",
292 "--size",
293 "--type",
294 "container",
295 "test-container"
296 ]
297 );
298 }
299}